├── .gitattributes ├── .github ├── CONTRIBUTING.md └── ISSUE_TEMPLATE.md ├── .gitignore ├── CHANGELOG.md ├── CssTools.sln ├── LICENSE ├── README.md ├── appveyor.yml ├── art ├── color-adornments.png ├── font-preview.png ├── image-preview.png └── mediatype.png ├── lib ├── Microsoft.CSS.Core.dll ├── Microsoft.CSS.Editor.dll ├── Microsoft.VisualStudio.Web.BrowserLink.12.0.dll ├── Microsoft.VisualStudio.Web.Extensions.dll ├── Microsoft.Web.Core.dll └── Microsoft.Web.Editor.dll └── src ├── Adornments ├── ColorAdornment.cs ├── ColorAdornmentTagger.cs ├── ColorAdornmentTaggerProvider.cs ├── ColorTag.cs ├── ColorTagger.cs ├── ColorTaggerProvider.cs └── IntraTextAdornmentTagger.cs ├── BrowserLink └── BrowserInfo │ ├── BrowserInfo.cs │ └── BrowserInfo.js ├── Classify ├── Base64TaggerProvider.cs ├── CssHighlightWordTagger.cs ├── EmbeddedImageClassifier.cs ├── ImportantClassifier.cs ├── ValueTaggerProvider.cs └── VendorClassifier.cs ├── Commands ├── ArrowsCommandTarget.cs ├── CssCreationListener.cs └── RetriggerCommandTarget.cs ├── Completion ├── BrowserCompletionListEntry.cs ├── CompletionListEntry.cs ├── CompletionProviders │ ├── BrowserLinkCompletionProvider.cs │ ├── ColorCompletionProvider.cs │ ├── FontCompletionProvider.cs │ ├── MediaCompletionProvider.cs │ ├── MediaTypeCompletionProvider.cs │ ├── MediaValueCompletionProvider.cs │ └── RegionCompletionProvider.cs ├── ContextProviders │ ├── LessPseudoContextProvider.cs │ ├── MediaFeatureContextProvider.cs │ └── MediaQueryContextProvider.cs ├── Filter │ ├── HideInheritInitialCompletionListFilter.cs │ ├── HideUncommonCompletionListFilter.cs │ ├── ObsoleteCompletionListFilter.cs │ └── WebkitScrollbarCompletionListFilter.cs ├── FontFamilyCompletionListEntry.cs └── RegionCompletionListEntry.cs ├── CssTools.csproj ├── CssToolsPackage.cs ├── DragDrop └── FontDrop.cs ├── ExtensionMethods ├── AtDirectiveExtensions.cs ├── ColorModelExtensions.cs ├── CssExtensions.cs ├── DeclarationExtensions.cs └── Extensions.cs ├── Helpers ├── CssItemAggregator.cs ├── CssItemCollector.cs ├── CssTreeWatcher.cs ├── FileHelpers.cs ├── OptionHelpers.cs ├── ProjectHelpers.cs └── VendorHelpers.cs ├── Properties └── AssemblyInfo.cs ├── QuickInfo ├── Font │ ├── FontQuickInfo.cs │ ├── FontQuickInfoController.cs │ ├── FontQuickInfoControllerProvider.cs │ └── FontQuickInfoSourceProvider.cs ├── Image │ ├── ImageQuickInfo.cs │ ├── ImageQuickInfoController.cs │ ├── ImageQuickInfoControllerProvider.cs │ └── ImageQuickInfoSourceProvider.cs ├── Selector │ ├── SelectorQuickInfo.cs │ ├── SelectorQuickInfoController.cs │ ├── SelectorQuickInfoControllerProvider.cs │ ├── SelectorQuickInfoSourceProvider.cs │ └── SelectorSpecificity.cs └── ValueOrder │ ├── RemoveCssSignatureHelpSource.cs │ ├── ValueOrderFactory.cs │ ├── ValueOrderSignature.cs │ ├── ValueOrderSignatureHelpSource.cs │ └── ValueOrderSignatureHelpSourceProvider.cs ├── Resources ├── Browsers │ ├── c.png │ ├── e.png │ ├── ff.png │ ├── ie.png │ ├── o.png │ └── s.png ├── Icon.png └── nopreview.png ├── 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 | packages 2 | 3 | # User files 4 | *.suo 5 | *.user 6 | *.sln.docstates 7 | .vs/ 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Rr]elease/ 12 | x64/ 13 | [Bb]in/ 14 | [Oo]bj/ 15 | 16 | # MSTest test Results 17 | [Tt]est[Rr]esult*/ 18 | [Bb]uild[Ll]og.* 19 | 20 | # NCrunch 21 | *.ncrunchsolution 22 | *.ncrunchproject 23 | _NCrunch_WebCompiler -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Road map 2 | 3 | - [ ] Light bulbs 4 | 5 | Features that have a checkmark are complete and available for 6 | download in the 7 | [CI build](http://vsixgallery.com/extension/0020efc9-e999-4e6f-a2b6-604127f480bc/). 8 | 9 | # Change log 10 | 11 | These are the changes to each version that has been released 12 | on the official Visual Studio extension gallery. 13 | 14 | ## 1.0 15 | 16 | - [x] Initial release 17 | - [x] Color adornments 18 | - [x] Font preview 19 | - [x] Image preview 20 | - [x] Selector specificity tooltip 21 | - [x] Property value signature help 22 | - [x] Selector highlighter 23 | - [x] Various classifiers 24 | - [x] Drag 'n drop font files 25 | - [x] Media query Intellisense powered by Browser Link 26 | - [x] Font import Intellisense 27 | - [x] Intellisense filters for unsupported properties 28 | - [x] Vendor property synchronization 29 | - [x] Darken/lighten hex colors -------------------------------------------------------------------------------- /CssTools.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.25921.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CssTools", "src\CssTools.csproj", "{43292B53-5CFB-42BD-9BF3-F3457251B608}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D1FDE0E6-30E3-474E-A176-5C5A56FBDDDF}" 9 | ProjectSection(SolutionItems) = preProject 10 | appveyor.yml = appveyor.yml 11 | CHANGELOG.md = CHANGELOG.md 12 | README.md = README.md 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Release|Any CPU = Release|Any CPU 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {43292B53-5CFB-42BD-9BF3-F3457251B608}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {43292B53-5CFB-42BD-9BF3-F3457251B608}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {43292B53-5CFB-42BD-9BF3-F3457251B608}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {43292B53-5CFB-42BD-9BF3-F3457251B608}.Release|Any CPU.Build.0 = Release|Any CPU 25 | EndGlobalSection 26 | GlobalSection(SolutionProperties) = preSolution 27 | HideSolutionNode = FALSE 28 | EndGlobalSection 29 | EndGlobal 30 | -------------------------------------------------------------------------------- /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 | # CSS Tools 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/qpdkwo2g3qov6ka8?svg=true)](https://ci.appveyor.com/project/madskristensen/csstools) 4 | 5 | Download this extension from the [VS Marketplace](https://marketplace.visualstudio.com/vsgallery/a2b0e9a8-85c6-4495-8578-dc1da0a8791c) 6 | or get the [CI build](http://vsixgallery.com/extension/0020efc9-e999-4e6f-a2b6-604127f480bc/). 7 | 8 | --------------------------------------- 9 | 10 | Provides additional features to the CSS editor in Visual Studio. 11 | 12 | See the [change log](CHANGELOG.md) for changes and road map. 13 | 14 | ## Features 15 | 16 | - Color adornments 17 | - Image and font preview 18 | - Selector specificity tooltip 19 | - Drag 'n drop of font files 20 | - Darken/lighten hex colors 21 | - Vendor property synchronization 22 | - Media query Intellisense powered by Browser Link 23 | 24 | ### Color adornments 25 | All color types are supported, including the new CSS 3 formats. 26 | 27 | ![Colors](art/color-adornments.png) 28 | 29 | ### Image and font preview 30 | ![Font preview](art/font-preview.png) 31 | 32 | ![Image preview](art/image-preview.png) 33 | 34 | ### Darken/lighten hex colors 35 | Place the caret on a hex color value and hit **Ctrl+Shift+Up/Down** to 36 | darken or lighten the color value. 37 | 38 | The same can be done with any number value such as `1.2px` to increase 39 | or decrease the value. 40 | 41 | ### Vendor property synchronization 42 | [![Vendor sync](http://img.youtube.com/vi/9NFKU7CCewE/0.jpg)](http://www.youtube.com/watch?v=9NFKU7CCewE) 43 | 44 | ### Media query Intellisense 45 | ![Media query type](art/mediatype.png) 46 | 47 | ## Contribute 48 | Check out the [contribution guidelines](.github/CONTRIBUTING.md) 49 | if you want to contribute to this project. 50 | 51 | For cloning and building this project yourself, make sure 52 | to install the 53 | [Extensibility Tools 2015](https://visualstudiogallery.msdn.microsoft.com/ab39a092-1343-46e2-b0f1-6a3f91155aa6) 54 | extension for Visual Studio which enables some features 55 | used by this project. 56 | 57 | ## License 58 | [Apache 2.0](LICENSE) -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /art/color-adornments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/art/color-adornments.png -------------------------------------------------------------------------------- /art/font-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/art/font-preview.png -------------------------------------------------------------------------------- /art/image-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/art/image-preview.png -------------------------------------------------------------------------------- /art/mediatype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/art/mediatype.png -------------------------------------------------------------------------------- /lib/Microsoft.CSS.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/lib/Microsoft.CSS.Core.dll -------------------------------------------------------------------------------- /lib/Microsoft.CSS.Editor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/lib/Microsoft.CSS.Editor.dll -------------------------------------------------------------------------------- /lib/Microsoft.VisualStudio.Web.BrowserLink.12.0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/lib/Microsoft.VisualStudio.Web.BrowserLink.12.0.dll -------------------------------------------------------------------------------- /lib/Microsoft.VisualStudio.Web.Extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/lib/Microsoft.VisualStudio.Web.Extensions.dll -------------------------------------------------------------------------------- /lib/Microsoft.Web.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/lib/Microsoft.Web.Core.dll -------------------------------------------------------------------------------- /lib/Microsoft.Web.Editor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/lib/Microsoft.Web.Editor.dll -------------------------------------------------------------------------------- /src/Adornments/ColorAdornment.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Editor.Completion; 2 | using Microsoft.VisualStudio.Text.Editor; 3 | using System; 4 | using System.Windows; 5 | using System.Windows.Controls; 6 | using System.Windows.Media; 7 | 8 | namespace CssTools 9 | { 10 | internal sealed class ColorAdornment : Border 11 | { 12 | private static SolidColorBrush _borderColor = OptionHelpers.BackgroundColor.Invert().ToBrush(); 13 | 14 | internal ColorAdornment(ColorTag colorTag, ITextView view) 15 | { 16 | this.Padding = new Thickness(0); 17 | this.BorderThickness = new Thickness(1); 18 | this.Margin = new Thickness(0, 0, 2, 3); 19 | this.Width = OptionHelpers.FontSize; 20 | this.Height = this.Width; 21 | this.Cursor = System.Windows.Input.Cursors.Arrow; 22 | this.MouseUp += delegate { ColorAdornmentMouseUp(view); }; 23 | 24 | Update(colorTag); 25 | } 26 | 27 | private static void ColorAdornmentMouseUp(ITextView view) 28 | { 29 | try 30 | { 31 | CssCompletionController.FromView(view).OnShowMemberList(filterList: true); 32 | } 33 | catch 34 | { } 35 | } 36 | 37 | internal void Update(ColorTag colorTag) 38 | { 39 | this.Background = new SolidColorBrush(colorTag.Color); 40 | 41 | if (!HasContrastToBackground(colorTag.Color)) 42 | { 43 | this.BorderThickness = new Thickness(1); 44 | this.BorderBrush = _borderColor; 45 | } 46 | else 47 | { 48 | this.BorderThickness = new Thickness(0); 49 | this.BorderBrush = this.Background; 50 | } 51 | } 52 | 53 | private static bool HasContrastToBackground(Color color) 54 | { 55 | // The color is very transparent (alpha channel) 56 | if (color.A < 13) 57 | { 58 | return false; 59 | } 60 | 61 | var b = OptionHelpers.BackgroundColor; 62 | double bBrightness = b.Red * 299 + b.Green * 587 + b.Blue * 114; 63 | double cBrightness = color.R * 299 + color.G * 587 + color.B * 114; 64 | double distance = Math.Abs(cBrightness - bBrightness) / 1000; 65 | 66 | return distance > 20; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Adornments/ColorAdornmentTagger.cs: -------------------------------------------------------------------------------- 1 | //*************************************************************************** 2 | // 3 | // Copyright (c) Microsoft Corporation. All rights reserved. 4 | // This code is licensed under the Visual Studio SDK license terms. 5 | // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 6 | // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY 7 | // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR 8 | // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. 9 | // 10 | //*************************************************************************** 11 | 12 | // This controls whether the adornments are positioned next to the hex values or instead of them. 13 | #define HIDING_TEXT 14 | 15 | using Microsoft.VisualStudio.Text; 16 | using Microsoft.VisualStudio.Text.Editor; 17 | using Microsoft.VisualStudio.Text.Tagging; 18 | using System; 19 | using System.Collections.Generic; 20 | 21 | namespace CssTools 22 | { 23 | /// 24 | /// Provides color swatch adornments in place of color constants. 25 | /// 26 | /// 27 | /// 28 | /// This is a sample usage of the utility class. 29 | /// 30 | /// 31 | internal sealed class ColorAdornmentTagger : IntraTextAdornmentTagger 32 | { 33 | private ITagAggregator colorTagger; 34 | 35 | internal static ITagger GetTagger(ITextView view, ITextBuffer buffer, Lazy> colorTagger) 36 | { 37 | return buffer.Properties.GetOrCreateSingletonProperty( 38 | () => new ColorAdornmentTagger(view, buffer, colorTagger.Value)); 39 | } 40 | 41 | private ColorAdornmentTagger(ITextView view, ITextBuffer buffer, ITagAggregator colorTagger) 42 | : base(view, buffer) 43 | { 44 | this.colorTagger = colorTagger; 45 | } 46 | 47 | public void Dispose() 48 | { 49 | this.colorTagger.Dispose(); 50 | base.buffer.Properties.RemoveProperty(typeof(ColorAdornmentTagger)); 51 | } 52 | 53 | // To produce adornments that don't obscure the text, the adornment tags 54 | // should have zero length spans. Overriding this method allows control 55 | // over the tag spans. 56 | protected override IEnumerable> GetAdornmentData(NormalizedSnapshotSpanCollection spans) 57 | { 58 | if (spans.Count == 0) 59 | yield break; 60 | 61 | ITextSnapshot snapshot = spans[0].Snapshot; 62 | var colorTags = this.colorTagger.GetTags(spans); 63 | 64 | foreach (IMappingTagSpan dataTagSpan in colorTags) 65 | { 66 | NormalizedSnapshotSpanCollection colorTagSpans = dataTagSpan.Span.GetSpans(snapshot); 67 | 68 | // Ignore data tags that are split by projection. 69 | // This is theoretically possible but unlikely in current scenarios. 70 | if (colorTagSpans.Count != 1) 71 | continue; 72 | 73 | SnapshotSpan adornmentSpan = new SnapshotSpan(colorTagSpans[0].Start, 0); 74 | 75 | yield return Tuple.Create(adornmentSpan, (PositionAffinity?)PositionAffinity.Successor, dataTagSpan.Tag); 76 | } 77 | } 78 | 79 | protected override ColorAdornment CreateAdornment(ColorTag dataTag, SnapshotSpan span) 80 | { 81 | return new ColorAdornment(dataTag, view); 82 | } 83 | 84 | protected override bool UpdateAdornment(ColorAdornment adornment, ColorTag dataTag) 85 | { 86 | if (adornment != null) 87 | { 88 | adornment.Update(dataTag); 89 | } 90 | 91 | return true; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Adornments/ColorAdornmentTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | //*************************************************************************** 2 | // 3 | // Copyright (c) Microsoft Corporation. All rights reserved. 4 | // This code is licensed under the Visual Studio SDK license terms. 5 | // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 6 | // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY 7 | // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR 8 | // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. 9 | // 10 | //*************************************************************************** 11 | 12 | using Microsoft.VisualStudio.Text; 13 | using Microsoft.VisualStudio.Text.Editor; 14 | using Microsoft.VisualStudio.Text.Tagging; 15 | using Microsoft.VisualStudio.Utilities; 16 | using System; 17 | using System.ComponentModel.Composition; 18 | 19 | namespace CssTools 20 | { 21 | [Export(typeof(IViewTaggerProvider))] 22 | [ContentType("css")] 23 | [TagType(typeof(IntraTextAdornmentTag))] 24 | [TextViewRole(PredefinedTextViewRoles.Document)] 25 | internal sealed class ColorAdornmentTaggerProvider : IViewTaggerProvider 26 | { 27 | #pragma warning disable 649 // "field never assigned to" -- field is set by MEF. 28 | [Import] 29 | internal IBufferTagAggregatorFactoryService BufferTagAggregatorFactoryService; 30 | #pragma warning restore 649 31 | 32 | public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) where T : ITag 33 | { 34 | if (textView == null) 35 | throw new ArgumentNullException("textView"); 36 | 37 | if (buffer == null) 38 | throw new ArgumentNullException("buffer"); 39 | 40 | if (textView.Roles.Contains("DIFF")) 41 | return null; 42 | 43 | return ColorAdornmentTagger.GetTagger( 44 | textView, buffer, 45 | new Lazy>( 46 | () => BufferTagAggregatorFactoryService.CreateTagAggregator(buffer))) 47 | as ITagger; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Adornments/ColorTag.cs: -------------------------------------------------------------------------------- 1 | //*************************************************************************** 2 | // 3 | // Copyright (c) Microsoft Corporation. All rights reserved. 4 | // This code is licensed under the Visual Studio SDK license terms. 5 | // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 6 | // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY 7 | // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR 8 | // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. 9 | // 10 | //*************************************************************************** 11 | 12 | using Microsoft.VisualStudio.Text.Tagging; 13 | using System.Windows.Media; 14 | 15 | namespace CssTools 16 | { 17 | /// 18 | /// Data tag indicating that the tagged text represents a color. 19 | /// 20 | /// 21 | /// Note that this tag has nothing directly to do with adornments or other UI. 22 | /// This sample's adornments will be produced based on the data provided in these tags. 23 | /// This separation provides the potential for other extensions to consume color tags 24 | /// and provide alternative UI or other derived functionality over this data. 25 | /// 26 | internal class ColorTag : ITag 27 | { 28 | internal readonly Color Color; 29 | 30 | internal ColorTag(Color color) 31 | { 32 | this.Color = color; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Adornments/ColorTagger.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.Document; 2 | using Microsoft.CSS.Core.Parser; 3 | using Microsoft.CSS.Core.Tokens; 4 | using Microsoft.CSS.Core.TreeItems; 5 | using Microsoft.CSS.Core.TreeItems.Functions; 6 | using Microsoft.CSS.Core.TreeItems.PropertyValues; 7 | using Microsoft.CSS.Editor.Completion.ColorPicker; 8 | using Microsoft.CSS.Editor.Document; 9 | using Microsoft.VisualStudio.Text; 10 | using Microsoft.VisualStudio.Text.Tagging; 11 | using Microsoft.Web.Editor.ColorPicker; 12 | using Microsoft.Web.Editor.Host; 13 | using Microsoft.Web.Languages.Css.Extensions; 14 | using System; 15 | using System.Collections.Generic; 16 | using System.Drawing; 17 | using System.Linq; 18 | using System.Windows.Threading; 19 | 20 | namespace CssTools 21 | { 22 | internal sealed class ColorTagger : ITagger 23 | { 24 | private ITextBuffer _buffer; 25 | public event EventHandler TagsChanged; 26 | 27 | internal ColorTagger(ITextBuffer buffer) 28 | { 29 | _buffer = buffer; 30 | _buffer.ChangedLowPriority += BufferChanged; 31 | } 32 | 33 | public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) 34 | { 35 | if (WebEditor.Host == null || spans.Count == 0 || spans[0].Length == 0 || spans[0].Length >= _buffer.CurrentSnapshot.Length) 36 | yield break; 37 | 38 | var tree = CssEditorDocument.FromTextBuffer(_buffer).Tree; 39 | IEnumerable items = GetColors(tree, spans[0]); 40 | 41 | foreach (var item in items.Where(i => (i.Start + i.Length) <= _buffer.CurrentSnapshot.Length)) 42 | { 43 | SnapshotSpan span = new SnapshotSpan(_buffer.CurrentSnapshot, item.Start, item.Length); 44 | ColorModel colorModel = ColorParser.TryParseColor(item, ColorParser.Options.AllowAlpha | ColorParser.Options.AllowNames); 45 | // Fix up for rebeccapurple adornment as it isn't parsed by ColorParser currently 46 | if (colorModel == null && item.Text=="rebeccapurple") 47 | { 48 | // as per http://lists.w3.org/Archives/Public/www-style/2014Jun/0312.html 49 | colorModel = ColorParser.TryParseColor("#663399", ColorParser.Options.AllowAlpha | ColorParser.Options.AllowNames); 50 | } 51 | if (colorModel != null) 52 | { 53 | yield return new TagSpan(span, new ColorTag(colorModel.Color)); 54 | } 55 | } 56 | } 57 | 58 | private static IEnumerable GetColors(CssTree tree, SnapshotSpan span) 59 | { 60 | ParseItem complexItem = tree.StyleSheet.ItemFromRange(span.Start, span.Length); 61 | 62 | if (complexItem == null) 63 | return Enumerable.Empty(); 64 | 65 | var colorCrawler = new CssItemAggregator(filter: e => e.AfterEnd > span.Start && e.Start < span.End) 66 | { 67 | (HexColorValue h) => h, 68 | (FunctionColor c) => c, 69 | (TokenItem i) => (i.PreviousSibling == null || (i.PreviousSibling.Text != "@" && i.PreviousSibling.Text != "$")) // Ignore variable names that happen to be colors 70 | && i.TokenType == CssTokenType.Identifier 71 | && (i.FindType() != null || i.FindType() != null) // Ignore classnames that happen to be colors 72 | && Color.FromName(i.Text).IsNamedColor 73 | ? i : null 74 | }; 75 | 76 | return colorCrawler.Crawl(complexItem).Where(o => o != null); 77 | } 78 | 79 | private void BufferChanged(object sender, TextContentChangedEventArgs e) 80 | { 81 | if (e.Changes.Count == 0) 82 | return; 83 | 84 | var temp = TagsChanged; 85 | if (temp == null) 86 | return; 87 | 88 | // Combine all changes into a single span so that 89 | // the ITagger<>.TagsChanged event can be raised just once for a compound edit 90 | // with many parts. 91 | 92 | ITextSnapshot snapshot = e.After; 93 | 94 | int start = e.Changes[0].NewPosition; 95 | int end = e.Changes[e.Changes.Count - 1].NewEnd; 96 | 97 | SnapshotSpan totalAffectedSpan = new SnapshotSpan( 98 | snapshot.GetLineFromPosition(start).Start, 99 | snapshot.GetLineFromPosition(end).End); 100 | 101 | //temp(this, new SnapshotSpanEventArgs(totalAffectedSpan)); 102 | 103 | Dispatcher.CurrentDispatcher.BeginInvoke( 104 | new Action(() => temp(this, new SnapshotSpanEventArgs(totalAffectedSpan))), DispatcherPriority.ApplicationIdle); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Adornments/ColorTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text; 2 | using Microsoft.VisualStudio.Text.Tagging; 3 | using Microsoft.VisualStudio.Utilities; 4 | using System; 5 | using System.ComponentModel.Composition; 6 | 7 | namespace CssTools 8 | { 9 | [Export(typeof(ITaggerProvider))] 10 | [ContentType("css")] 11 | [TagType(typeof(ColorTag))] 12 | internal sealed class ColorTaggerProvider : ITaggerProvider 13 | { 14 | public ITagger CreateTagger(ITextBuffer buffer) where T : ITag 15 | { 16 | if (buffer == null) 17 | throw new ArgumentNullException("buffer"); 18 | 19 | return buffer.Properties.GetOrCreateSingletonProperty(() => new ColorTagger(buffer)) as ITagger; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/BrowserLink/BrowserInfo/BrowserInfo.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Web.BrowserLink; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Composition; 4 | using System.IO; 5 | 6 | namespace CssTools 7 | { 8 | [Export(typeof(IBrowserLinkExtensionFactory))] 9 | public class BrowserInfoFactory : IBrowserLinkExtensionFactory 10 | { 11 | public BrowserLinkExtension CreateExtensionInstance(BrowserLinkConnection connection) 12 | { 13 | return new BrowserInfo(); 14 | } 15 | 16 | public string GetScript() 17 | { 18 | using (Stream stream = GetType().Assembly.GetManifestResourceStream("CssTools.BrowserLink.BrowserInfo.BrowserInfo.js")) 19 | using (StreamReader reader = new StreamReader(stream)) 20 | { 21 | return reader.ReadToEnd(); 22 | } 23 | } 24 | } 25 | 26 | public class BrowserInfo : BrowserLinkExtension 27 | { 28 | private static Dictionary _browserCapDictionary = new Dictionary(); 29 | private BrowserLinkConnection _current; 30 | 31 | public static Dictionary BrowserCapDictionary { get { return _browserCapDictionary; } } 32 | 33 | public override void OnConnected(BrowserLinkConnection connection) 34 | { 35 | _current = connection; 36 | 37 | base.OnConnected(connection); 38 | } 39 | 40 | public override void OnDisconnecting(BrowserLinkConnection connection) 41 | { 42 | if (_browserCapDictionary.ContainsKey(connection.ConnectionId)) 43 | _browserCapDictionary.Remove(connection.ConnectionId); 44 | 45 | base.OnDisconnecting(connection); 46 | } 47 | 48 | [BrowserLinkCallback] // This method can be called from JavaScript 49 | public void CollectInfo(int width, int height) 50 | { 51 | var browserCap = new BrowserCap 52 | { 53 | Name = _current.AppName, 54 | Width = width, 55 | Height = height, 56 | }; 57 | 58 | _browserCapDictionary[_current.ConnectionId] = browserCap; 59 | } 60 | } 61 | 62 | public class BrowserCap 63 | { 64 | public string Name { get; set; } 65 | public int Width { get; set; } 66 | public int Height { get; set; } 67 | } 68 | } -------------------------------------------------------------------------------- /src/BrowserLink/BrowserInfo/BrowserInfo.js: -------------------------------------------------------------------------------- 1 | (function (browserLink, $) { 2 | /// 3 | /// 4 | 5 | function SendInfo() { 6 | 7 | var width = window.innerWidth || document.body.clientWidth; 8 | var height = window.innerHeight || document.body.clientHeight; 9 | 10 | browserLink.invoke("CollectInfo", width, height); 11 | } 12 | 13 | window.addEventListener('resize', function (event) { 14 | SendInfo(); 15 | }); 16 | 17 | return { 18 | 19 | onConnected: function () { // Optional. Is called when a connection is established 20 | SendInfo(); 21 | } 22 | }; 23 | }); -------------------------------------------------------------------------------- /src/Classify/Base64TaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.Document; 2 | using Microsoft.CSS.Core.TreeItems.Functions; 3 | using Microsoft.CSS.Editor.Document; 4 | using Microsoft.VisualStudio.Text; 5 | using Microsoft.VisualStudio.Text.Tagging; 6 | using Microsoft.VisualStudio.Utilities; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.ComponentModel.Composition; 10 | using System.Linq; 11 | using System.Windows.Controls; 12 | 13 | namespace CssTools 14 | { 15 | [Export(typeof(ITaggerProvider))] 16 | [TagType(typeof(IOutliningRegionTag))] 17 | [ContentType("CSS")] 18 | internal sealed class Base64TaggerProvider : ITaggerProvider 19 | { 20 | public ITagger CreateTagger(ITextBuffer buffer) where T : ITag 21 | { 22 | return buffer.Properties.GetOrCreateSingletonProperty(() => new Base64Tagger(buffer)) as ITagger; 23 | } 24 | } 25 | 26 | internal sealed class Base64Tagger : ITagger 27 | { 28 | private ITextBuffer buffer; 29 | private CssTree _tree; 30 | 31 | public Base64Tagger(ITextBuffer buffer) 32 | { 33 | this.buffer = buffer; 34 | this.buffer.Changed += BufferChanged; 35 | } 36 | 37 | public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) 38 | { 39 | if (spans.Count == 0 || !EnsureInitialized()) 40 | yield break; 41 | 42 | var visitor = new CssItemCollector(); 43 | _tree.StyleSheet.Accept(visitor); 44 | 45 | foreach (UrlItem url in visitor.Items.Where(u => u.UrlString != null && u.Start >= spans[0].Start)) 46 | { 47 | if (url.UrlString.Text.IndexOf("base64,") > -1 && buffer.CurrentSnapshot.Length >= url.UrlString.AfterEnd) 48 | { 49 | var items = new List(); 50 | ImageQuickInfo.AddImageContent(items, url.UrlString.Text.Trim('"', '\'')); 51 | 52 | // Replace any TextBuffers into strings for the tooltip to display. 53 | // This works because base64 images are loaded synchronously, so we 54 | // can compute the size before returning. If they do change, we'll 55 | // need to replace them with TextBlocks & handle the Changed event. 56 | for (int i = 0; i < items.Count; i++) 57 | { 58 | var tipBuffer = items[i] as ITextBuffer; 59 | if (tipBuffer == null) 60 | continue; 61 | items[i] = tipBuffer.CurrentSnapshot.GetText(); 62 | } 63 | var content = new ItemsControl { ItemsSource = items }; 64 | 65 | var span = new SnapshotSpan(new SnapshotPoint(buffer.CurrentSnapshot, url.UrlString.Start), url.UrlString.Length); 66 | var tag = new OutliningRegionTag(true, true, url.UrlString.Length + " characters", content); 67 | yield return new TagSpan(span, tag); 68 | } 69 | } 70 | } 71 | 72 | public bool EnsureInitialized() 73 | { 74 | if (_tree == null) 75 | { 76 | try 77 | { 78 | CssEditorDocument document = CssEditorDocument.FromTextBuffer(buffer); 79 | _tree = document.Tree; 80 | } 81 | catch (Exception) 82 | { 83 | } 84 | } 85 | 86 | return _tree != null; 87 | } 88 | 89 | public event EventHandler TagsChanged 90 | { 91 | add { } 92 | remove { } 93 | } 94 | 95 | void BufferChanged(object sender, TextContentChangedEventArgs e) 96 | { 97 | if (e.After != buffer.CurrentSnapshot) 98 | return; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Classify/ImportantClassifier.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.Document; 2 | using Microsoft.CSS.Core.Parser; 3 | using Microsoft.CSS.Core.TreeItems; 4 | using Microsoft.CSS.Core.Utilities; 5 | using Microsoft.VisualStudio.Text; 6 | using Microsoft.VisualStudio.Text.Classification; 7 | using Microsoft.VisualStudio.Utilities; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.ComponentModel.Composition; 11 | using System.Linq; 12 | 13 | namespace CssTools 14 | { 15 | public static class ImportantClassificationType 16 | { 17 | public const string Name = "css-important"; 18 | 19 | [Export, Name(ImportantClassificationType.Name)] 20 | public static ClassificationTypeDefinition Definition { get; set; } 21 | } 22 | 23 | [Export(typeof(IClassifierProvider))] 24 | [ContentType("css")] 25 | public sealed class ImportantClassifierProvider : IClassifierProvider 26 | { 27 | [Import] 28 | public IClassificationTypeRegistryService Registry { get; set; } 29 | 30 | public IClassifier GetClassifier(ITextBuffer textBuffer) 31 | { 32 | return textBuffer.Properties.GetOrCreateSingletonProperty(() => new ImportantClassifier(Registry, textBuffer)); 33 | } 34 | } 35 | 36 | internal sealed class ImportantClassifier : IClassifier 37 | { 38 | private readonly IClassificationTypeRegistryService _registry; 39 | private readonly ITextBuffer _buffer; 40 | private readonly CssTreeWatcher _tree; 41 | private readonly SortedRangeList _cache = new SortedRangeList(); 42 | private readonly IClassificationType _importantClassification; 43 | 44 | public event EventHandler ClassificationChanged 45 | { 46 | add { } 47 | remove { } 48 | } 49 | 50 | internal ImportantClassifier(IClassificationTypeRegistryService registry, ITextBuffer buffer) 51 | { 52 | _registry = registry; 53 | _buffer = buffer; 54 | _importantClassification = _registry.GetClassificationType(ImportantClassificationType.Name); 55 | 56 | _tree = CssTreeWatcher.ForBuffer(_buffer); 57 | _tree.TreeUpdated += TreeUpdated; 58 | _tree.ItemsChanged += TreeItemsChanged; 59 | UpdateCache(_tree.StyleSheet); 60 | } 61 | 62 | public IList GetClassificationSpans(SnapshotSpan span) 63 | { 64 | List spans = new List(); 65 | 66 | foreach (TokenItem item in _cache) 67 | { 68 | int start = span.Start.Position; 69 | int end = span.End.Position; 70 | 71 | if (item.Start >= start && item.AfterEnd <= end) 72 | { 73 | var snapShotSpan = new SnapshotSpan(span.Snapshot, item.Start - 1, item.Length + 1); 74 | var classSpan = new ClassificationSpan(snapShotSpan, _importantClassification); 75 | spans.Add(classSpan); 76 | } 77 | } 78 | 79 | return spans; 80 | } 81 | 82 | private void UpdateCache(ParseItem item) 83 | { 84 | var visitor = new CssItemCollector(true); 85 | item.Accept(visitor); 86 | 87 | foreach (TokenItem token in visitor.Items.Where(d => d.Important != null).Select(d => d.Important)) 88 | { 89 | if (!_cache.Contains(token)) 90 | _cache.Add(token); 91 | } 92 | } 93 | 94 | private void TreeUpdated(object sender, CssTreeUpdateEventArgs e) 95 | { 96 | _cache.Clear(); 97 | UpdateCache(e.Tree.StyleSheet); 98 | } 99 | 100 | private void TreeItemsChanged(object sender, CssItemsChangedEventArgs e) 101 | { 102 | foreach (ParseItem item in e.DeletedItems) 103 | { 104 | var matches = _cache.Where(s => s.Start >= item.Start && s.AfterEnd <= item.AfterEnd); 105 | foreach (var match in matches.Reverse()) 106 | { 107 | _cache.Remove(match); 108 | } 109 | } 110 | 111 | foreach (ParseItem item in e.InsertedItems) 112 | { 113 | UpdateCache(item); 114 | } 115 | } 116 | } 117 | 118 | [Export(typeof(EditorFormatDefinition))] 119 | [UserVisible(true)] 120 | [ClassificationType(ClassificationTypeNames = ImportantClassificationType.Name)] 121 | [Name(ImportantClassificationType.Name)] 122 | [Order(After = Priority.Default)] 123 | internal sealed class ImportantFormatDefinition : ClassificationFormatDefinition 124 | { 125 | public ImportantFormatDefinition() 126 | { 127 | IsBold = true; 128 | DisplayName = "CSS !important"; 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /src/Classify/ValueTaggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.TreeItems; 2 | using Microsoft.VisualStudio.Text; 3 | using Microsoft.VisualStudio.Text.Classification; 4 | using Microsoft.VisualStudio.Text.Editor; 5 | using Microsoft.VisualStudio.Text.Tagging; 6 | using Microsoft.VisualStudio.Utilities; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.ComponentModel.Composition; 10 | using System.Linq; 11 | using System.Windows.Threading; 12 | 13 | namespace CssTools 14 | { 15 | [Export(typeof(IViewTaggerProvider))] 16 | [ContentType("css")] 17 | [TagType(typeof(TextMarkerTag))] 18 | internal class VendorTaggerProvider : IViewTaggerProvider 19 | { 20 | 21 | public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) where T : ITag 22 | { 23 | if (textView == null) 24 | { 25 | return null; 26 | } 27 | 28 | return buffer.Properties.GetOrCreateSingletonProperty(() => new VendorTagger(textView, buffer)) as ITagger; 29 | } 30 | } 31 | 32 | internal class VendorTagger : ITagger 33 | { 34 | ITextView View { get; set; } 35 | ITextBuffer Buffer { get; set; } 36 | SnapshotPoint? CurrentChar { get; set; } 37 | private readonly VendorClassifier _vendorClassifier; 38 | private bool _pendingUpdate = false; 39 | 40 | internal VendorTagger(ITextView view, ITextBuffer buffer) 41 | { 42 | View = view; 43 | Buffer = buffer; 44 | CurrentChar = null; 45 | buffer.Properties.TryGetProperty(typeof(VendorClassifier), out _vendorClassifier); 46 | 47 | View.Caret.PositionChanged += CaretPositionChanged; 48 | View.LayoutChanged += ViewLayoutChanged; 49 | } 50 | 51 | public event EventHandler TagsChanged; 52 | 53 | void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) 54 | { 55 | if (e.NewSnapshot != e.OldSnapshot) 56 | { 57 | UpdateAtCaretPosition(View.Caret.Position); 58 | } 59 | } 60 | 61 | void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) 62 | { 63 | UpdateAtCaretPosition(e.NewPosition); 64 | } 65 | 66 | void UpdateAtCaretPosition(CaretPosition caretPosition) 67 | { 68 | if (!_pendingUpdate) 69 | { 70 | _pendingUpdate = true; 71 | CurrentChar = caretPosition.Point.GetPoint(this.Buffer, caretPosition.Affinity); 72 | if (!CurrentChar.HasValue) 73 | { 74 | return; 75 | } 76 | 77 | Dispatcher.CurrentDispatcher.BeginInvoke( 78 | new Action(() => Update()), DispatcherPriority.ContextIdle); 79 | } 80 | } 81 | 82 | private void Update() 83 | { 84 | var tempEvent = TagsChanged; 85 | if (tempEvent != null) 86 | { 87 | SnapshotSpan span = new SnapshotSpan(this.Buffer.CurrentSnapshot, 0, this.Buffer.CurrentSnapshot.Length); 88 | tempEvent(this, new SnapshotSpanEventArgs(span)); 89 | _pendingUpdate = false; 90 | } 91 | } 92 | 93 | public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) 94 | { 95 | if (spans.Count == 0 || Buffer.CurrentSnapshot.Length == 0) 96 | yield break; 97 | 98 | if (!CurrentChar.HasValue || CurrentChar.Value.Position >= CurrentChar.Value.Snapshot.Length) 99 | yield break; 100 | 101 | SnapshotPoint currentChar = CurrentChar.Value; 102 | if (spans[0].Snapshot != currentChar.Snapshot) 103 | { 104 | currentChar = currentChar.TranslateTo(spans[0].Snapshot, PointTrackingMode.Positive); 105 | } 106 | 107 | var allTags = _vendorClassifier.GetClassificationSpans(spans[0]).Where(s => s.ClassificationType.Classification == VendorClassificationTypes.Value); 108 | foreach (var tagSpan in allTags) 109 | { 110 | if (tagSpan.Span.Contains(currentChar)) 111 | { 112 | Declaration dec = _vendorClassifier.Cache.FirstOrDefault(e => currentChar.Position > e.Start && currentChar.Position < e.AfterEnd); 113 | if (dec != null && dec.PropertyName.Text.Length > 0 && !dec.IsVendorSpecific()) 114 | { 115 | foreach (Declaration vendor in _vendorClassifier.Cache.Where(d => d.Parent == dec.Parent && VendorClassifier.GetStandardName(d) == dec.PropertyName.Text)) 116 | { 117 | // Manage quotes for -ms-filter 118 | string value = Buffer.CurrentSnapshot.GetText(vendor.Colon.AfterEnd, vendor.AfterEnd - vendor.Colon.AfterEnd); 119 | int quotes = value.StartsWith("'") || value.StartsWith("\"") ? 1 : 0; 120 | SnapshotSpan vendorSpan = new SnapshotSpan(Buffer.CurrentSnapshot, vendor.Colon.AfterEnd + quotes, vendor.AfterEnd - vendor.Colon.AfterEnd - (quotes * 2)); 121 | yield return new TagSpan(vendorSpan, new TextMarkerTag("vendorhighlight")); 122 | } 123 | 124 | SnapshotSpan s = tagSpan.Span; 125 | yield return new TagSpan(s, new TextMarkerTag("vendorhighlight")); 126 | yield break; 127 | } 128 | } 129 | } 130 | } 131 | } 132 | 133 | [Export(typeof(EditorFormatDefinition))] 134 | [Name("vendorhighlight")] 135 | [UserVisible(true)] 136 | internal class HighlightWordFormatDefinition : MarkerFormatDefinition 137 | { 138 | public HighlightWordFormatDefinition() 139 | { 140 | this.DisplayName = "CSS Property Value Highlight"; 141 | } 142 | 143 | } 144 | } -------------------------------------------------------------------------------- /src/Classify/VendorClassifier.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.Document; 2 | using Microsoft.CSS.Core.Parser; 3 | using Microsoft.CSS.Core.TreeItems; 4 | using Microsoft.CSS.Core.Utilities; 5 | using Microsoft.VisualStudio.Text; 6 | using Microsoft.VisualStudio.Text.Classification; 7 | using Microsoft.VisualStudio.Utilities; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.ComponentModel.Composition; 11 | using System.Linq; 12 | using System.Windows.Threading; 13 | 14 | namespace CssTools 15 | { 16 | public static class VendorClassificationTypes 17 | { 18 | public const string Declaration = "vendor.declaration"; 19 | public const string Value = "vendor.value"; 20 | 21 | [Export, Name(VendorClassificationTypes.Declaration)] 22 | public static ClassificationTypeDefinition VendorDeclarationClassificationType { get; set; } 23 | 24 | [Export, Name(VendorClassificationTypes.Value)] 25 | public static ClassificationTypeDefinition VendorValueClassificationType { get; set; } 26 | } 27 | 28 | [Export(typeof(IClassifierProvider))] 29 | [ContentType("css")] 30 | public sealed class VendorClassifierProvider : IClassifierProvider 31 | { 32 | [Import] 33 | public IClassificationTypeRegistryService Registry { get; set; } 34 | 35 | public IClassifier GetClassifier(ITextBuffer textBuffer) 36 | { 37 | return textBuffer.Properties.GetOrCreateSingletonProperty(() => { return new VendorClassifier(Registry, textBuffer); }); 38 | } 39 | } 40 | 41 | internal sealed class VendorClassifier : IClassifier 42 | { 43 | private readonly IClassificationTypeRegistryService _registry; 44 | private readonly ITextBuffer _buffer; 45 | private readonly CssTreeWatcher _tree; 46 | internal readonly SortedRangeList Cache = new SortedRangeList(); 47 | private readonly IClassificationType _decClassification; 48 | private readonly IClassificationType _valClassification; 49 | 50 | internal VendorClassifier(IClassificationTypeRegistryService registry, ITextBuffer buffer) 51 | { 52 | _registry = registry; 53 | _buffer = buffer; 54 | _decClassification = _registry.GetClassificationType(VendorClassificationTypes.Declaration); 55 | _valClassification = _registry.GetClassificationType(VendorClassificationTypes.Value); 56 | 57 | _tree = CssTreeWatcher.ForBuffer(_buffer); 58 | _tree.TreeUpdated += TreeUpdated; 59 | _tree.ItemsChanged += TreeItemsChanged; 60 | UpdateDeclarationCache(_tree.StyleSheet); 61 | 62 | } 63 | 64 | public IList GetClassificationSpans(SnapshotSpan span) 65 | { 66 | List spans = new List(); 67 | 68 | foreach (Declaration dec in Cache.Where(d => d.PropertyName.Text.Length > 0 && span.Start <= d.Start && span.End >= d.AfterEnd)) 69 | { 70 | if (dec.IsVendorSpecific()) 71 | { 72 | var ss = new SnapshotSpan(span.Snapshot, dec.Start, dec.Length); 73 | var s = new ClassificationSpan(ss, _decClassification); 74 | spans.Add(s); 75 | } 76 | 77 | if (dec.Semicolon == null) 78 | continue; 79 | 80 | int start = dec.Colon.AfterEnd; 81 | int length = dec.AfterEnd - start; 82 | if (span.Snapshot.Length > start + length) 83 | { 84 | var ss2 = new SnapshotSpan(span.Snapshot, start, length); 85 | var s2 = new ClassificationSpan(ss2, _valClassification); 86 | spans.Add(s2); 87 | } 88 | } 89 | 90 | return spans; 91 | } 92 | 93 | public static string GetStandardName(Declaration dec) 94 | { 95 | string name = dec.PropertyName.Text; 96 | if (name.Length > 0 && name[0] == '-') 97 | { 98 | int index = name.IndexOf('-', 1) + 1; 99 | name = index > -1 ? name.Substring(index) : name; 100 | } 101 | 102 | return name; 103 | } 104 | 105 | 106 | private void UpdateDeclarationCache(ParseItem item) 107 | { 108 | var visitor = new CssItemCollector(true); 109 | item.Accept(visitor); 110 | 111 | HashSet rules = new HashSet(); 112 | 113 | foreach (Declaration dec in visitor.Items) 114 | { 115 | RuleBlock rule = dec.Parent as RuleBlock; 116 | if (rule == null || rules.Contains(rule)) 117 | continue; 118 | 119 | var vendors = rule.GetDeclarations().Where(d => d.IsValid && d.IsVendorSpecific()); 120 | foreach (Declaration vendor in vendors) 121 | { 122 | string name = GetStandardName(vendor); 123 | Declaration standard = rule.GetDeclarations().FirstOrDefault(d => d.IsValid && d.PropertyName.Text == name); 124 | 125 | if (standard != null) 126 | { 127 | if (!Cache.Contains(standard)) 128 | Cache.Add(standard); 129 | 130 | if (GetValueText(standard) == GetValueText(vendor) && !Cache.Contains(vendor)) 131 | Cache.Add(vendor); 132 | } 133 | } 134 | 135 | rules.Add(rule); 136 | } 137 | } 138 | 139 | private void TreeUpdated(object sender, CssTreeUpdateEventArgs e) 140 | { 141 | Cache.Clear(); 142 | UpdateDeclarationCache(e.Tree.StyleSheet); 143 | } 144 | 145 | private void TreeItemsChanged(object sender, CssItemsChangedEventArgs e) 146 | { 147 | foreach (ParseItem item in e.DeletedItems) 148 | { 149 | if (Cache.Contains(item)) 150 | Cache.Remove((Declaration)item); 151 | } 152 | 153 | foreach (ParseItem item in e.InsertedItems) 154 | { 155 | UpdateDeclarationCache(item); 156 | UpdateVendorValues(item); 157 | } 158 | } 159 | 160 | private void UpdateVendorValues(ParseItem item) 161 | { 162 | Declaration dec = item.FindType(); 163 | if (dec != null && Cache.Contains(dec) && !dec.IsVendorSpecific()) 164 | { 165 | // Find all vendor specifics that isn't the standard property. 166 | var matches = Cache.Where(d => d.IsValid && d != dec && d.Parent == dec.Parent && GetStandardName(d) == dec.PropertyName.Text && d.PropertyName.Text != dec.PropertyName.Text); 167 | 168 | // Undo sometimes messes with the positions, so we have to make this check before proceeding. 169 | if (!matches.Any() || dec.Text.Length < dec.Colon.AfterEnd - dec.Start || dec.Colon.AfterEnd < dec.Start) 170 | return; 171 | 172 | string text = dec.Text.Substring(dec.Colon.AfterEnd - dec.Start, dec.AfterEnd - dec.Colon.AfterEnd); 173 | using (ITextEdit edit = _buffer.CreateEdit()) 174 | { 175 | foreach (Declaration match in matches.Reverse()) 176 | { 177 | SnapshotSpan span = new SnapshotSpan(_buffer.CurrentSnapshot, match.Colon.AfterEnd, match.AfterEnd - match.Colon.AfterEnd); 178 | if (span.GetText() != text) 179 | edit.Replace(span, text); 180 | } 181 | 182 | edit.Apply(); 183 | } 184 | } 185 | } 186 | 187 | private string GetValueText(Declaration dec) 188 | { 189 | int start = dec.Colon.AfterEnd; 190 | int length = dec.AfterEnd - start; 191 | return _buffer.CurrentSnapshot.GetText(start, length); 192 | } 193 | 194 | public event EventHandler ClassificationChanged; 195 | 196 | public void RaiseClassificationChanged(SnapshotSpan span) 197 | { 198 | var handler = this.ClassificationChanged; 199 | if (handler != null) 200 | { 201 | Dispatcher.CurrentDispatcher.BeginInvoke( 202 | new Action(() => handler(this, new ClassificationChangedEventArgs(span))), DispatcherPriority.ApplicationIdle); 203 | } 204 | } 205 | } 206 | 207 | [Export(typeof(EditorFormatDefinition))] 208 | [UserVisible(true)] 209 | [ClassificationType(ClassificationTypeNames = VendorClassificationTypes.Declaration)] 210 | [Name(VendorClassificationTypes.Declaration)] 211 | [Order(After = Priority.Default)] 212 | internal sealed class VendorDeclarationFormatDefinition : ClassificationFormatDefinition 213 | { 214 | public VendorDeclarationFormatDefinition() 215 | { 216 | ForegroundOpacity = 0.5; 217 | DisplayName = "CSS Vendor Property"; 218 | } 219 | } 220 | } -------------------------------------------------------------------------------- /src/Commands/ArrowsCommandTarget.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.Parser; 2 | using Microsoft.CSS.Core.TreeItems.Functions; 3 | using Microsoft.CSS.Core.TreeItems.PropertyValues; 4 | using Microsoft.CSS.Editor.Completion.ColorPicker; 5 | using Microsoft.CSS.Editor.Document; 6 | using Microsoft.VisualStudio; 7 | using Microsoft.VisualStudio.OLE.Interop; 8 | using Microsoft.VisualStudio.Text; 9 | using Microsoft.VisualStudio.Text.Editor; 10 | using Microsoft.VisualStudio.TextManager.Interop; 11 | using Microsoft.Web.Core.ContentTypes; 12 | using Microsoft.Web.Editor.Host; 13 | using System; 14 | using System.Globalization; 15 | using Editor = Microsoft.Web.Editor.ColorPicker; 16 | 17 | namespace CssTools 18 | { 19 | class ArrowsCommandTarget : IOleCommandTarget 20 | { 21 | private ITextView _textView; 22 | private IOleCommandTarget _nextCommandTarget; 23 | 24 | public ArrowsCommandTarget(IVsTextView adapter, ITextView textView) 25 | { 26 | this._textView = textView; 27 | ErrorHandler.ThrowOnFailure(adapter.AddCommandFilter(this, out _nextCommandTarget)); 28 | } 29 | 30 | public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) 31 | { 32 | if (pguidCmdGroup == typeof(VSConstants.VSStd2KCmdID).GUID) 33 | { 34 | switch (nCmdID) 35 | { 36 | case 2400: 37 | if (Move(Direction.Down)) 38 | return VSConstants.S_OK; 39 | break; 40 | 41 | case 2401: 42 | if (Move(Direction.Up)) 43 | return VSConstants.S_OK; 44 | break; 45 | } 46 | } 47 | 48 | return _nextCommandTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); 49 | } 50 | 51 | private enum Direction 52 | { 53 | Up, 54 | Down 55 | } 56 | 57 | private bool Move(Direction direction) 58 | { 59 | if (WebEditor.Host == null) 60 | return false; 61 | 62 | var point = _textView.BufferGraph.MapDownToInsertionPoint(_textView.Caret.Position.BufferPosition, PointTrackingMode.Positive, ts => ts.ContentType.IsOfType(CssContentTypeDefinition.CssContentType)); 63 | if (point == null) 64 | return false; 65 | 66 | var tree = CssEditorDocument.FromTextBuffer(point.Value.Snapshot.TextBuffer); 67 | ParseItem item = tree.StyleSheet.ItemBeforePosition(point.Value.Position); 68 | if (item == null) 69 | return false; 70 | 71 | NumericalValue unit = item.FindType(); 72 | if (unit != null) 73 | { 74 | return HandleUnits(direction, unit, point.Value.Snapshot); 75 | } 76 | 77 | HexColorValue hex = item.FindType(); 78 | if (hex != null) 79 | { 80 | return HandleHex(direction, hex, point.Value.Snapshot); 81 | } 82 | 83 | return false; 84 | } 85 | 86 | private static bool HandleUnits(Direction direction, NumericalValue item, ITextSnapshot snapshot) 87 | { 88 | float value; 89 | if (!float.TryParse(item.Number.Text, out value)) 90 | return false; 91 | 92 | if (!AreWithinLimits(direction, value, item)) 93 | return true; 94 | 95 | var span = new SnapshotSpan(snapshot, item.Number.Start, item.Number.Length); 96 | float delta = GetDelta(item.Number.Text); 97 | string format = item.Number.Text.Contains(".") ? "#.#0" : string.Empty; 98 | if (NumberDecimalPlaces(item.Number.Text) == 1) 99 | format = "F1"; 100 | 101 | if (direction == Direction.Down) 102 | UpdateSpan(span, (value - delta).ToString(format, CultureInfo.InvariantCulture), "Decrease value"); 103 | else 104 | UpdateSpan(span, (value + delta).ToString(format, CultureInfo.InvariantCulture), "Increase value"); 105 | 106 | return true; 107 | } 108 | 109 | private static int NumberDecimalPlaces(string value) 110 | { 111 | int s = value.IndexOf(".", StringComparison.CurrentCulture) + 1; // the first numbers plus decimal point 112 | if (s == 0) // No decimal point 113 | return 0; 114 | 115 | return value.Length - s; //total length minus beginning numbers and decimal = number of decimal points 116 | } 117 | 118 | private static bool AreWithinLimits(Direction direction, float number, NumericalValue item) 119 | { 120 | UnitType type = GetUnitType(item); 121 | switch (type) 122 | { 123 | case UnitType.Angle: 124 | return (direction == Direction.Up) ? number < 360 : number > -360; 125 | 126 | //case UnitType.Percentage: 127 | // return (direction == Direction.Up) ? number < 100 : number > 0; 128 | 129 | // Larger than zero 130 | case UnitType.Grid: 131 | case UnitType.Frequency: 132 | case UnitType.Resolution: 133 | case UnitType.Time: 134 | return (direction == Direction.Down) ? number > 0 : true; 135 | 136 | case UnitType.Percentage: 137 | case UnitType.Length: 138 | case UnitType.Viewport: 139 | return true; 140 | } 141 | 142 | FunctionColor func = item.FindType(); 143 | if (func != null) 144 | { 145 | if (func.FunctionName.Text.StartsWith("rgb", StringComparison.Ordinal)) 146 | { 147 | if (direction == Direction.Up) 148 | return number < 255; 149 | else 150 | return number > 0; 151 | } 152 | 153 | if (func.FunctionName.Text.StartsWith("hsl", StringComparison.Ordinal)) 154 | { 155 | if (direction == Direction.Up) 156 | return number < 360; 157 | else 158 | return number > 0; 159 | } 160 | } 161 | 162 | return true; 163 | } 164 | 165 | private static UnitType GetUnitType(ParseItem valueItem) 166 | { 167 | UnitValue unitValue = valueItem as UnitValue; 168 | 169 | return (unitValue != null) ? unitValue.UnitType : UnitType.Unknown; 170 | } 171 | 172 | private static bool HandleHex(Direction direction, HexColorValue item, ITextSnapshot snapshot) 173 | { 174 | var model = ColorParser.TryParseColor(item.Text, ColorParser.Options.None); 175 | 176 | if (model != null) 177 | { 178 | var span = new SnapshotSpan(snapshot, item.Start, item.Length); 179 | 180 | if (direction == Direction.Down && model.HslLightness > 0) 181 | { 182 | model.Format = Editor.ColorFormat.RgbHex3; 183 | UpdateSpan(span, Editor.ColorFormatter.FormatColor(model.Darken(), model.Format), "Darken color"); 184 | } 185 | else if (direction == Direction.Up && model.HslLightness < 1) 186 | { 187 | model.Format = Editor.ColorFormat.RgbHex3; 188 | UpdateSpan(span, Editor.ColorFormatter.FormatColor(model.Brighten(), model.Format), "Brighten color"); 189 | } 190 | 191 | return true; 192 | } 193 | 194 | return false; 195 | } 196 | 197 | private static float GetDelta(string value) 198 | { 199 | int decimals = NumberDecimalPlaces(value); 200 | if (decimals > 0) 201 | { 202 | if (decimals > 1) 203 | return 0.01F; 204 | else 205 | return 0.1F; 206 | } 207 | 208 | return 1F; 209 | } 210 | 211 | private static void UpdateSpan(SnapshotSpan span, string result, string undoTitle) 212 | { 213 | if (result.Length > 1) 214 | result = result.TrimStart('0'); 215 | 216 | try 217 | { 218 | ProjectHelpers.DTE.UndoContext.Open(undoTitle); 219 | span.Snapshot.TextBuffer.Replace(span, result); 220 | } 221 | finally 222 | { 223 | ProjectHelpers.DTE.UndoContext.Close(); 224 | } 225 | } 226 | 227 | public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) 228 | { 229 | if (pguidCmdGroup == typeof(VSConstants.VSStd2KCmdID).GUID) 230 | { 231 | for (int i = 0; i < cCmds; i++) 232 | { 233 | switch (prgCmds[i].cmdID) 234 | { 235 | case 2401: // Up 236 | case 2400: // Down 237 | prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); 238 | return VSConstants.S_OK; 239 | } 240 | } 241 | } 242 | 243 | return _nextCommandTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); 244 | } 245 | } 246 | } -------------------------------------------------------------------------------- /src/Commands/CssCreationListener.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Editor; 2 | using Microsoft.VisualStudio.Text; 3 | using Microsoft.VisualStudio.Text.Editor; 4 | using Microsoft.VisualStudio.Utilities; 5 | using Microsoft.Web.Core.ContentTypes; 6 | using System.Collections.ObjectModel; 7 | using System.ComponentModel.Composition; 8 | using System.Diagnostics.CodeAnalysis; 9 | using System.Linq; 10 | 11 | namespace CssTools 12 | { 13 | [Export(typeof(IWpfTextViewConnectionListener))] 14 | [ContentType(CssContentTypeDefinition.CssContentType)] 15 | [ContentType(HtmlContentTypeDefinition.HtmlContentType)] 16 | [TextViewRole(PredefinedTextViewRoles.Document)] 17 | public class CssConnectionListener : IWpfTextViewConnectionListener 18 | { 19 | [Import] 20 | public IVsEditorAdaptersFactoryService EditorAdaptersFactoryService { get; set; } 21 | 22 | [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] 23 | public void SubjectBuffersConnected(IWpfTextView textView, ConnectionReason reason, Collection subjectBuffers) 24 | { 25 | if (!subjectBuffers.Any(b => b.ContentType.IsOfType(CssContentTypeDefinition.CssContentType))) 26 | return; 27 | 28 | var textViewAdapter = EditorAdaptersFactoryService.GetViewAdapter(textView); 29 | if (textViewAdapter == null) 30 | return; 31 | 32 | //textView.Properties.GetOrCreateSingletonProperty(() => new ExtractToFile(textViewAdapter, textView)); 33 | //textView.Properties.GetOrCreateSingletonProperty(() => new CssAddMissingStandard(textViewAdapter, textView)); 34 | //textView.Properties.GetOrCreateSingletonProperty(() => new CssAddMissingVendor(textViewAdapter, textView)); 35 | //textView.Properties.GetOrCreateSingletonProperty(() => new CssRemoveDuplicates(textViewAdapter, textView)); 36 | //textView.Properties.GetOrCreateSingletonProperty(() => new MinifySelection(textViewAdapter, textView)); 37 | //textView.Properties.GetOrCreateSingletonProperty(() => new CssFindReferences(textViewAdapter, textView)); 38 | //textView.Properties.GetOrCreateSingletonProperty(() => new F1Help(textViewAdapter, textView)); 39 | //textView.Properties.GetOrCreateSingletonProperty(() => new CssSelectBrowsers(textViewAdapter, textView)); 40 | textView.Properties.GetOrCreateSingletonProperty(() => new RetriggerTarget(textViewAdapter, textView)); 41 | textView.Properties.GetOrCreateSingletonProperty(() => new ArrowsCommandTarget(textViewAdapter, textView)); 42 | } 43 | 44 | public void SubjectBuffersDisconnected(IWpfTextView textView, ConnectionReason reason, Collection subjectBuffers) 45 | { 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Commands/RetriggerCommandTarget.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Editor.Completion; 2 | using Microsoft.VisualStudio; 3 | using Microsoft.VisualStudio.OLE.Interop; 4 | using Microsoft.VisualStudio.Text.Editor; 5 | using Microsoft.VisualStudio.TextManager.Interop; 6 | using System; 7 | using System.Runtime.InteropServices; 8 | using System.Windows.Threading; 9 | 10 | namespace CssTools 11 | { 12 | internal class RetriggerTarget : IOleCommandTarget 13 | { 14 | private ITextView _textView; 15 | private IOleCommandTarget _nextCommandTarget; 16 | 17 | public RetriggerTarget(IVsTextView adapter, ITextView textView) 18 | { 19 | _textView = textView; 20 | ErrorHandler.ThrowOnFailure(adapter.AddCommandFilter(this, out _nextCommandTarget)); 21 | } 22 | 23 | public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) 24 | { 25 | if (pguidCmdGroup == VSConstants.VSStd2K && nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR) 26 | { 27 | char typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); 28 | 29 | switch (typedChar) 30 | { 31 | case '!': 32 | case '=': 33 | //case '/': 34 | case '[': 35 | Retrigger(); 36 | break; 37 | } 38 | } 39 | 40 | return _nextCommandTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); 41 | } 42 | 43 | private void Retrigger() 44 | { 45 | Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => 46 | { 47 | var controller = CssCompletionController.FromView(_textView); 48 | 49 | if (controller != null) 50 | controller.OnShowMemberList(true); 51 | 52 | }), DispatcherPriority.Normal, null); 53 | } 54 | 55 | public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) 56 | { 57 | return _nextCommandTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/Completion/BrowserCompletionListEntry.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Editor.Completion; 2 | using Microsoft.CSS.Editor.Schemas.Browsers; 3 | using Microsoft.VisualStudio.Language.Intellisense; 4 | using Microsoft.VisualStudio.Text; 5 | using Microsoft.Web.Editor.Completion; 6 | using Microsoft.Web.Editor.Imaging; 7 | using System; 8 | using System.Windows.Media; 9 | using System.Windows.Media.Imaging; 10 | 11 | namespace CssTools 12 | { 13 | internal class BrowserCompletionListEntry : ICssCompletionListEntry 14 | { 15 | private string _insertion; 16 | private ImageSource _icon; 17 | 18 | public BrowserCompletionListEntry(string name, string browserName) 19 | { 20 | this.DisplayText = name + " (from " + browserName + ")"; 21 | _insertion = name; 22 | SetIcon(browserName.ToLowerInvariant()); 23 | } 24 | 25 | private void SetIcon(string browserName) 26 | { 27 | if (browserName.Contains("internet explorer")) 28 | _icon = BitmapFrame.Create(new Uri("pack://application:,,,/CssTools;component/Resources/Browsers/ie.png", UriKind.RelativeOrAbsolute)); 29 | else if (browserName.Contains("firefox")) 30 | _icon = BitmapFrame.Create(new Uri("pack://application:,,,/CssTools;component/Resources/Browsers/ff.png", UriKind.RelativeOrAbsolute)); 31 | else if (browserName.Contains("opera")) 32 | _icon = BitmapFrame.Create(new Uri("pack://application:,,,/CssTools;component/Resources/Browsers/o.png", UriKind.RelativeOrAbsolute)); 33 | else if (browserName.Contains("chrome")) 34 | _icon = BitmapFrame.Create(new Uri("pack://application:,,,/CssTools;component/Resources/Browsers/c.png", UriKind.RelativeOrAbsolute)); 35 | else if (browserName.Contains("safari")) 36 | _icon = BitmapFrame.Create(new Uri("pack://application:,,,/CssTools;component/Resources/Browsers/s.png", UriKind.RelativeOrAbsolute)); 37 | else if (browserName.Contains("microsoft edge")) 38 | _icon = BitmapFrame.Create(new Uri("pack://application:,,,/CssTools;component/Resources/Browsers/e.png", UriKind.RelativeOrAbsolute)); 39 | else 40 | _icon = GlyphService.GetGlyph(StandardGlyphGroup.GlyphGroupEnumMember, StandardGlyphItem.GlyphItemPublic); 41 | } 42 | 43 | public string Description { get; set; } 44 | 45 | public string DisplayText { get; set; } 46 | 47 | public string GetSyntax(Version version) 48 | { 49 | return string.Empty; 50 | } 51 | 52 | public string GetAttribute(string name) 53 | { 54 | return string.Empty; 55 | } 56 | 57 | public string GetInsertionText(CssTextSource textSource, ITrackingSpan typingSpan) 58 | { 59 | return _insertion; 60 | } 61 | 62 | public string GetVersionedAttribute(string name, Version version) 63 | { 64 | return GetAttribute(name); 65 | } 66 | 67 | public bool AllowQuotedString 68 | { 69 | get { return false; } 70 | } 71 | 72 | public bool IsBuilder 73 | { 74 | get { return false; } 75 | } 76 | 77 | public int SortingPriority 78 | { 79 | get { return -1; } 80 | } 81 | 82 | 83 | public bool IsSupported(BrowserVersion browser) 84 | { 85 | return true; 86 | } 87 | 88 | public bool IsSupported(Version cssVersion) 89 | { 90 | return true; 91 | } 92 | 93 | public ITrackingSpan ApplicableTo 94 | { 95 | get { return null; } 96 | } 97 | 98 | public CompletionEntryFilterTypes FilterType 99 | { 100 | get { return CompletionEntryFilterTypes.AlwaysVisible; } 101 | } 102 | 103 | public ImageSource Icon 104 | { 105 | get { return _icon; } 106 | } 107 | 108 | public bool IsCommitChar(char typedCharacter) 109 | { 110 | return false; 111 | } 112 | 113 | public bool IsMuteCharacter(char typedCharacter) 114 | { 115 | return false; 116 | } 117 | 118 | public bool RetriggerIntellisense 119 | { 120 | get { return false; } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/Completion/CompletionListEntry.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Editor.Completion; 2 | using Microsoft.CSS.Editor.Schemas.Browsers; 3 | using Microsoft.VisualStudio.Language.Intellisense; 4 | using Microsoft.VisualStudio.Text; 5 | using Microsoft.Web.Editor.Completion; 6 | using Microsoft.Web.Editor.Imaging; 7 | using System; 8 | using System.Windows.Media; 9 | 10 | namespace CssTools 11 | { 12 | internal class CompletionListEntry : ICssCompletionListEntry 13 | { 14 | private string _name; 15 | private StandardGlyphGroup _glyph; 16 | 17 | public CompletionListEntry(string name, int sortingPriority = 0, StandardGlyphGroup glyph = StandardGlyphGroup.GlyphGroupEnumMember) 18 | { 19 | _name = name; 20 | _glyph = glyph; 21 | SortingPriority = sortingPriority; 22 | } 23 | 24 | public string Description { get; set; } 25 | 26 | public string DisplayText 27 | { 28 | get { return _name; } 29 | } 30 | 31 | public string GetSyntax(Version version) 32 | { 33 | return string.Empty; 34 | } 35 | 36 | public string GetAttribute(string name) 37 | { 38 | return string.Empty; 39 | } 40 | 41 | public string GetInsertionText(CssTextSource textSource, ITrackingSpan typingSpan) 42 | { 43 | return DisplayText; 44 | } 45 | 46 | public string GetVersionedAttribute(string name, Version version) 47 | { 48 | return GetAttribute(name); 49 | } 50 | 51 | public bool AllowQuotedString 52 | { 53 | get { return false; } 54 | } 55 | 56 | public bool IsBuilder 57 | { 58 | get { return false; } 59 | } 60 | 61 | public int SortingPriority { get; set; } 62 | 63 | 64 | public bool IsSupported(BrowserVersion browser) 65 | { 66 | return true; 67 | } 68 | 69 | public bool IsSupported(Version cssVersion) 70 | { 71 | return true; 72 | } 73 | 74 | public ITrackingSpan ApplicableTo 75 | { 76 | get { return null; } 77 | } 78 | 79 | public CompletionEntryFilterTypes FilterType 80 | { 81 | get { return CompletionEntryFilterTypes.MatchTyping; } 82 | } 83 | 84 | public ImageSource Icon 85 | { 86 | get { return GlyphService.GetGlyph(_glyph, StandardGlyphItem.GlyphItemPublic); } 87 | } 88 | 89 | public bool IsCommitChar(char typedCharacter) 90 | { 91 | return false; 92 | } 93 | 94 | public bool IsMuteCharacter(char typedCharacter) 95 | { 96 | return false; 97 | } 98 | 99 | public bool RetriggerIntellisense 100 | { 101 | get { return false; } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Completion/CompletionProviders/BrowserLinkCompletionProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.TreeItems; 2 | using Microsoft.CSS.Editor.Completion; 3 | using Microsoft.VisualStudio.Utilities; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel.Composition; 7 | using System.Linq; 8 | 9 | namespace CssTools 10 | { 11 | [Export(typeof(ICssCompletionProvider))] 12 | [Name("BrowserLinkCompletionProvider")] 13 | internal class BrowserLinkCompletionProvider : ICssCompletionListProvider 14 | { 15 | public CssCompletionContextType ContextType 16 | { 17 | get { return CssCompletionContextType.PropertyValue; } 18 | } 19 | 20 | public IEnumerable GetListEntries(CssCompletionContext context) 21 | { 22 | Declaration dec = context.ContextItem.FindType(); 23 | 24 | if (dec == null || dec.PropertyName == null) 25 | yield break; 26 | 27 | if (dec.PropertyName.Text.EndsWith("width", StringComparison.OrdinalIgnoreCase)) 28 | { 29 | foreach (var browser in BrowserInfo.BrowserCapDictionary.Values.OrderByDescending(b => b.Width)) 30 | { 31 | string value = browser.Width + "px"; 32 | yield return new BrowserCompletionListEntry(value, browser.Name); 33 | } 34 | } 35 | else if (dec.PropertyName.Text.EndsWith("height", StringComparison.OrdinalIgnoreCase)) 36 | { 37 | foreach (var browser in BrowserInfo.BrowserCapDictionary.Values.OrderByDescending(b => b.Height)) 38 | { 39 | string value = browser.Height + "px"; 40 | yield return new BrowserCompletionListEntry(value, browser.Name); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Completion/CompletionProviders/ColorCompletionProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Composition; 4 | using System.Drawing; 5 | using System.Reflection; 6 | using Microsoft.CSS.Editor; 7 | using Microsoft.VisualStudio.Language.Intellisense; 8 | using Microsoft.VisualStudio.Utilities; 9 | using Microsoft.CSS.Editor.Completion; 10 | using Microsoft.Web.Editor.Completion.Presenter; 11 | using Microsoft.CSS.Editor.Classify; 12 | 13 | namespace CssTools 14 | { 15 | [Export(typeof(ICssCompletionProvider))] 16 | [Name("ColorCompletionProvider")] 17 | [Order(Before = "Default PropertyValue")] 18 | internal class ColorCompletionProvider : ICssCompletionListProvider, ICssCompletionPresenterProvider 19 | { 20 | public CssCompletionContextType ContextType 21 | { 22 | get { return CssCompletionContextType.PropertyValue; } 23 | } 24 | 25 | public IEnumerable GetListEntries(CssCompletionContext context) 26 | { 27 | yield break; 28 | } 29 | 30 | public CompletionPresenterInfo TryCreateCompletionPresenter(ICompletionSession session, CssCompletionContext context) 31 | { 32 | string text = context.Snapshot.GetText(context.SpanStart, context.SpanLength); 33 | if (Color.FromName(text).IsKnownColor) 34 | { 35 | return CreatePresenter(session, context); 36 | } 37 | 38 | return new CompletionPresenterInfo(null, true); 39 | } 40 | 41 | private static CompletionPresenterInfo CreatePresenter(ICompletionSession session, CssCompletionContext context) 42 | { 43 | object[] parameters = new object[] { session, context }; 44 | BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; 45 | Type type = typeof(CssClassifier).Assembly.GetType("Microsoft.CSS.Editor.Completion.ColorPicker.ColorPickerPresenter"); 46 | object colorPicker = Activator.CreateInstance(type, flags, null, parameters, null); 47 | 48 | return new CompletionPresenterInfo((IIntellisensePresenter)colorPicker, false); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/Completion/CompletionProviders/FontCompletionProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.TreeItems; 2 | using Microsoft.CSS.Editor.Completion; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Text.Editor; 5 | using Microsoft.VisualStudio.Utilities; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.ComponentModel.Composition; 9 | using System.IO; 10 | using System.Windows.Forms; 11 | using System.Windows.Threading; 12 | 13 | namespace CssTools 14 | { 15 | [Export(typeof(ICssCompletionProvider))] 16 | [Name("FontCompletionProvider")] 17 | internal class FontCompletionProvider : ICssCompletionListProvider, ICssCompletionCommitListener 18 | { 19 | public CssCompletionContextType ContextType 20 | { 21 | get { return CssCompletionContextType.PropertyValue; } 22 | } 23 | 24 | public static bool IsFontFamilyContext(CssCompletionContext context) 25 | { 26 | if (context != null && context.ContextItem != null) 27 | { 28 | Declaration decl = context.ContextItem.Parent as Declaration; 29 | string propertyName = (decl != null && decl.PropertyName != null) ? decl.PropertyName.Text : string.Empty; 30 | 31 | // Currently, only "font-family" will show font names, so just hard-code that name. 32 | if (propertyName == "font-family") 33 | { 34 | return true; 35 | } 36 | } 37 | 38 | return false; 39 | } 40 | 41 | public IEnumerable GetListEntries(CssCompletionContext context) 42 | { 43 | if (!IsFontFamilyContext(context)) 44 | yield break; 45 | 46 | yield return new FontFamilyCompletionListEntry("Pick from file..."); 47 | } 48 | 49 | public void OnCommitted(ICssCompletionListEntry entry, ITrackingSpan contextSpan, SnapshotPoint caret, ITextView textView) 50 | { 51 | if (entry.DisplayText == "Pick from file...") 52 | { 53 | string fontFamily; 54 | string atDirective = GetFontFromFile(entry.DisplayText, (IWpfTextView)textView, out fontFamily); 55 | if (atDirective == null) 56 | return; // If the user cancelled the dialog, do nothing. 57 | 58 | Dispatcher.CurrentDispatcher.BeginInvoke( 59 | new Action(() => Replace(contextSpan, textView, atDirective, fontFamily)), DispatcherPriority.Normal); 60 | } 61 | } 62 | 63 | private static void Replace(ITrackingSpan contextSpan, ITextView textView, string atDirective, string fontFamily) 64 | { 65 | ProjectHelpers.DTE.UndoContext.Open("Embed font"); 66 | textView.TextBuffer.Insert(0, atDirective + Environment.NewLine + Environment.NewLine); 67 | textView.TextBuffer.Insert(contextSpan.GetSpan(textView.TextBuffer.CurrentSnapshot).Start, fontFamily); 68 | ProjectHelpers.DTE.UndoContext.Close(); 69 | } 70 | 71 | private static readonly object _syncRoot = new object(); 72 | private static string GetFontFromFile(string text, IWpfTextView view, out string fontFamily) 73 | { 74 | lock (_syncRoot) 75 | { 76 | fontFamily = text; 77 | using (OpenFileDialog dialog = new OpenFileDialog()) 78 | { 79 | dialog.InitialDirectory = Path.GetDirectoryName(ProjectHelpers.DTE.ActiveDocument.FullName); 80 | dialog.Filter = "Fonts (*.woff;*.eot;*.ttf;*.otf;*.svg)|*.woff;*.eot;*.ttf;*.otf;*.svg"; 81 | dialog.DefaultExt = ".woff"; 82 | 83 | if (dialog.ShowDialog() != DialogResult.OK) 84 | return null; 85 | 86 | FontDropHandler fdh = new FontDropHandler(view); 87 | return fdh.GetCodeFromFile(dialog.FileName, out fontFamily); 88 | } 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Completion/CompletionProviders/MediaCompletionProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.TreeItems.AtDirectives; 2 | using Microsoft.CSS.Editor.Completion; 3 | using Microsoft.VisualStudio.Utilities; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.Composition; 6 | 7 | namespace CssTools 8 | { 9 | [Export(typeof(ICssCompletionProvider))] 10 | [Name("MediaCompletionProvider")] 11 | internal class MediaCompletionProvider : ICssCompletionListProvider 12 | { 13 | public CssCompletionContextType ContextType 14 | { 15 | get { return (CssCompletionContextType)610; } 16 | } 17 | 18 | public IEnumerable GetListEntries(CssCompletionContext context) 19 | { 20 | MediaExpression expression = (MediaExpression)context.ContextItem; 21 | 22 | yield return new CompletionListEntry("device-width"); 23 | yield return new CompletionListEntry("max-device-width"); 24 | yield return new CompletionListEntry("min-device-width"); 25 | 26 | yield return new CompletionListEntry("device-height"); 27 | yield return new CompletionListEntry("max-device-height"); 28 | yield return new CompletionListEntry("min-device-height"); 29 | 30 | yield return new CompletionListEntry("height"); 31 | yield return new CompletionListEntry("max-height"); 32 | yield return new CompletionListEntry("min-height"); 33 | 34 | yield return new CompletionListEntry("width"); 35 | yield return new CompletionListEntry("max-width"); 36 | yield return new CompletionListEntry("min-width"); 37 | 38 | yield return new CompletionListEntry("orientation"); 39 | yield return new CompletionListEntry("scan"); 40 | yield return new CompletionListEntry("grid"); 41 | 42 | yield return new CompletionListEntry("resolution"); 43 | yield return new CompletionListEntry("max-resolution"); 44 | yield return new CompletionListEntry("min-resolution"); 45 | 46 | yield return new CompletionListEntry("aspect-ratio"); 47 | yield return new CompletionListEntry("max-aspect-ratio"); 48 | yield return new CompletionListEntry("min-aspect-ratio"); 49 | yield return new CompletionListEntry("device-aspect-ratio"); 50 | yield return new CompletionListEntry("max-device-aspect-ratio"); 51 | yield return new CompletionListEntry("min-device-aspect-ratio"); 52 | 53 | yield return new CompletionListEntry("color"); 54 | yield return new CompletionListEntry("color-index"); 55 | yield return new CompletionListEntry("min-color"); 56 | yield return new CompletionListEntry("max-color"); 57 | 58 | yield return new CompletionListEntry("color-index"); 59 | yield return new CompletionListEntry("max-color-index"); 60 | yield return new CompletionListEntry("min-color-index"); 61 | 62 | yield return new CompletionListEntry("update-frequency"); 63 | yield return new CompletionListEntry("overflow-block"); 64 | yield return new CompletionListEntry("overflow-inline"); 65 | 66 | yield return new CompletionListEntry("monochrome"); 67 | yield return new CompletionListEntry("max-monochrome"); 68 | yield return new CompletionListEntry("min-monochrome"); 69 | 70 | yield return new CompletionListEntry("pointer"); 71 | yield return new CompletionListEntry("any-pointer"); 72 | yield return new CompletionListEntry("hover"); 73 | yield return new CompletionListEntry("any-hover"); 74 | yield return new CompletionListEntry("light-level"); 75 | yield return new CompletionListEntry("scripting"); 76 | 77 | // Internet Explorer 78 | yield return new CompletionListEntry("-ms-high-contrast"); 79 | 80 | // Webkit 81 | yield return new CompletionListEntry("-webkit-device-pixel-ratio"); 82 | yield return new CompletionListEntry("-webkit-max-device-pixel-ratio"); 83 | yield return new CompletionListEntry("-webkit-min-device-pixel-ratio"); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Completion/CompletionProviders/MediaTypeCompletionProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.Composition; 3 | using Microsoft.CSS.Core; 4 | using Microsoft.VisualStudio.Language.Intellisense; 5 | using Microsoft.VisualStudio.Utilities; 6 | using Microsoft.CSS.Core.Parser; 7 | using Microsoft.CSS.Core.TreeItems.AtDirectives; 8 | using Microsoft.CSS.Editor.Completion; 9 | 10 | namespace CssTools 11 | { 12 | [Export(typeof(ICssCompletionProvider))] 13 | [Name("MediaTypeCompletionProvider")] 14 | internal class MediaTypeCompletionProvider : ICssCompletionListProvider 15 | { 16 | public CssCompletionContextType ContextType 17 | { 18 | get { return (CssCompletionContextType)612; } 19 | } 20 | 21 | public IEnumerable GetListEntries(CssCompletionContext context) 22 | { 23 | MediaQuery query = (MediaQuery)context.ContextItem; 24 | ParseItem item = query.StyleSheet.ItemAfterPosition(context.SpanStart); 25 | 26 | if (query.MediaType != null && query.MediaType.AfterEnd < item.Start) 27 | { 28 | yield return new CompletionListEntry("and", 0, StandardGlyphGroup.GlyphGroupOperator); 29 | yield break; 30 | } 31 | 32 | if (item != query.Operation || query.MediaType == null) 33 | { 34 | yield return new CompletionListEntry("all"); 35 | yield return new CompletionListEntry("aural"); 36 | yield return new CompletionListEntry("braille"); 37 | yield return new CompletionListEntry("embossed"); 38 | yield return new CompletionListEntry("handheld"); 39 | yield return new CompletionListEntry("print"); 40 | yield return new CompletionListEntry("projection"); 41 | yield return new CompletionListEntry("screen"); 42 | yield return new CompletionListEntry("tty"); 43 | yield return new CompletionListEntry("tv"); 44 | } 45 | 46 | if (item != query.MediaType || query.Operation == null) 47 | { 48 | yield return new CompletionListEntry("not", 1, StandardGlyphGroup.GlyphGroupOperator); 49 | yield return new CompletionListEntry("only", 1, StandardGlyphGroup.GlyphGroupOperator); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Completion/CompletionProviders/MediaValueCompletionProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.TreeItems.AtDirectives; 2 | using Microsoft.CSS.Editor.Completion; 3 | using Microsoft.VisualStudio.Utilities; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.Composition; 6 | using System.Linq; 7 | 8 | namespace CssTools 9 | { 10 | [Export(typeof(ICssCompletionProvider))] 11 | [Name("MediaValueCompletionProvider")] 12 | internal class MediaValueCompletionProvider : ICssCompletionListProvider 13 | { 14 | public CssCompletionContextType ContextType 15 | { 16 | get { return (CssCompletionContextType)611; } 17 | } 18 | 19 | public IEnumerable GetListEntries(CssCompletionContext context) 20 | { 21 | MediaExpression expression = (MediaExpression)context.ContextItem; 22 | 23 | switch (expression.MediaFeature.Text) 24 | { 25 | case "orientation": 26 | yield return new CompletionListEntry("portrait"); 27 | yield return new CompletionListEntry("landscape"); 28 | break; 29 | 30 | case "scan": 31 | yield return new CompletionListEntry("interlace"); 32 | yield return new CompletionListEntry("progressive"); 33 | break; 34 | 35 | case "-ms-high-contrast": 36 | yield return new CompletionListEntry("active"); 37 | yield return new CompletionListEntry("black-on-white"); 38 | yield return new CompletionListEntry("white-on-black"); 39 | yield return new CompletionListEntry("none"); 40 | break; 41 | 42 | case "update-frequency": 43 | yield return new CompletionListEntry("none"); 44 | yield return new CompletionListEntry("normal"); 45 | yield return new CompletionListEntry("slow"); 46 | break; 47 | 48 | case "overflow-block": 49 | yield return new CompletionListEntry("none"); 50 | yield return new CompletionListEntry("scroll"); 51 | yield return new CompletionListEntry("optional-paged"); 52 | yield return new CompletionListEntry("paged"); 53 | break; 54 | 55 | case "overflow-inline": 56 | yield return new CompletionListEntry("none"); 57 | yield return new CompletionListEntry("scroll"); 58 | break; 59 | 60 | case "pointer": 61 | case "any-pointer": 62 | yield return new CompletionListEntry("none"); 63 | yield return new CompletionListEntry("coarse"); 64 | yield return new CompletionListEntry("fine"); 65 | break; 66 | 67 | case "hover": 68 | case "any-hover": 69 | yield return new CompletionListEntry("none"); 70 | yield return new CompletionListEntry("on-demand"); 71 | yield return new CompletionListEntry("hover"); 72 | break; 73 | 74 | case "light-level": 75 | yield return new CompletionListEntry("dim"); 76 | yield return new CompletionListEntry("normal"); 77 | yield return new CompletionListEntry("washed"); 78 | break; 79 | 80 | case "scripting": 81 | yield return new CompletionListEntry("none"); 82 | yield return new CompletionListEntry("initial-only"); 83 | yield return new CompletionListEntry("enabled"); 84 | break; 85 | 86 | case "width": 87 | case "max-width": 88 | case "min-width": 89 | case "device-width": 90 | case "max-device-width": 91 | case "min-device-width": 92 | foreach (var browser in BrowserInfo.BrowserCapDictionary.Values.OrderByDescending(b => b.Width)) 93 | { 94 | string value = browser.Width + "px"; 95 | yield return new BrowserCompletionListEntry(value, browser.Name); 96 | } 97 | break; 98 | 99 | case "height": 100 | case "max-height": 101 | case "min-height": 102 | case "device-height": 103 | case "max-device-height": 104 | case "min-device-height": 105 | foreach (var browser in BrowserInfo.BrowserCapDictionary.Values.OrderByDescending(b => b.Height)) 106 | { 107 | string value = browser.Height + "px"; 108 | yield return new BrowserCompletionListEntry(value, browser.Name); 109 | } 110 | break; 111 | } 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/Completion/CompletionProviders/RegionCompletionProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Editor.Completion; 2 | using Microsoft.VisualStudio.Text; 3 | using Microsoft.VisualStudio.Text.Editor; 4 | using Microsoft.VisualStudio.Utilities; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel.Composition; 8 | using System.Windows.Threading; 9 | 10 | namespace CssTools 11 | { 12 | [Export(typeof(ICssCompletionProvider))] 13 | [Name("RegionCompletionProvider")] 14 | internal class RegionCompletionProvider : ICssCompletionListProvider, ICssCompletionCommitListener 15 | { 16 | private RegionCompletionListEntry _entry = new RegionCompletionListEntry(); 17 | 18 | public CssCompletionContextType ContextType 19 | { 20 | get { return CssCompletionContextType.ItemNameSelector; } 21 | } 22 | 23 | public IEnumerable GetListEntries(CssCompletionContext context) 24 | { 25 | var line = context.Snapshot.GetLineFromPosition(context.ContextItem.Start); 26 | string text = line.GetText().Trim(); 27 | 28 | if (text.Length == context.ContextItem.Length) 29 | { 30 | yield return _entry; 31 | } 32 | } 33 | 34 | public void OnCommitted(ICssCompletionListEntry entry, ITrackingSpan contextSpan, SnapshotPoint caret, ITextView textView) 35 | { 36 | if (entry.DisplayText == "Add region...") 37 | { 38 | Dispatcher.CurrentDispatcher.BeginInvoke( 39 | new Action(() => System.Windows.Forms.SendKeys.Send("{TAB}")), DispatcherPriority.Normal); 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/Completion/ContextProviders/LessPseudoContextProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.Parser; 2 | using Microsoft.CSS.Core.TreeItems; 3 | using Microsoft.CSS.Core.TreeItems.Selectors; 4 | using Microsoft.CSS.Editor.Completion; 5 | using Microsoft.VisualStudio.Utilities; 6 | using Microsoft.Web.Languages.Less.Parser; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.ComponentModel.Composition; 10 | 11 | namespace CssTools 12 | { 13 | [Export(typeof(ICssCompletionContextProvider))] 14 | [Name("LessPseudoContextProvider")] 15 | [Order(Before = "Default Pseudo")] 16 | internal class LessPseudoContextProvider : ICssCompletionContextProvider 17 | { 18 | public IEnumerable ItemTypes 19 | { 20 | get 21 | { 22 | return new Type[] 23 | { 24 | typeof(PseudoClassFunctionSelector), 25 | typeof(PseudoClassSelector), 26 | typeof(PseudoElementFunctionSelector), 27 | typeof(PseudoElementSelector) 28 | }; 29 | } 30 | } 31 | 32 | public CssCompletionContext GetCompletionContext(ParseItem item, int position) 33 | { 34 | RuleSet rule = item.FindType(); 35 | 36 | if (rule != null && rule.Parent is LessRuleBlock) 37 | { 38 | return new CssCompletionContext(CssCompletionContextType.Invalid, item.Start, item.Length, item); 39 | } 40 | 41 | return null; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/Completion/ContextProviders/MediaFeatureContextProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.Parser; 2 | using Microsoft.CSS.Core.TreeItems.AtDirectives; 3 | using Microsoft.CSS.Editor.Completion; 4 | using Microsoft.VisualStudio.Utilities; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel.Composition; 8 | 9 | namespace CssTools 10 | { 11 | [Export(typeof(ICssCompletionContextProvider))] 12 | [Name("MediaFeatureCompletionContextProvider")] 13 | internal class MediaFeatureCompletionContextProvider : ICssCompletionContextProvider 14 | { 15 | public IEnumerable ItemTypes 16 | { 17 | get 18 | { 19 | return new Type[] { typeof(MediaExpression), }; 20 | } 21 | } 22 | 23 | public CssCompletionContext GetCompletionContext(ParseItem item, int position) 24 | { 25 | MediaExpression expr = (MediaExpression)item; 26 | 27 | if (expr.MediaFeature != null && 28 | (position >= expr.Values.TextStart && position <= expr.Values.TextAfterEnd) || 29 | (expr.Colon != null && position >= expr.Colon.AfterEnd)) 30 | { 31 | return new CssCompletionContext((CssCompletionContextType)611, expr.Values.TextStart, expr.Values.TextLength, null); 32 | } 33 | 34 | if (expr.OpenFunctionBrace == null || position < expr.OpenFunctionBrace.Start) 35 | return null; 36 | 37 | int length = expr.MediaFeature != null ? expr.MediaFeature.Length : 1; 38 | 39 | return new CssCompletionContext((CssCompletionContextType)610, expr.OpenFunctionBrace.AfterEnd, length, null); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Completion/ContextProviders/MediaQueryContextProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.Parser; 2 | using Microsoft.CSS.Core.TreeItems.AtDirectives; 3 | using Microsoft.CSS.Editor.Completion; 4 | using Microsoft.VisualStudio.Utilities; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel.Composition; 8 | 9 | namespace CssTools 10 | { 11 | [Export(typeof(ICssCompletionContextProvider))] 12 | [Name("MediaQueryCompletionContextProvider")] 13 | internal class MediaQueryCompletionContextProvider : ICssCompletionContextProvider 14 | { 15 | public IEnumerable ItemTypes 16 | { 17 | get 18 | { 19 | return new Type[] { typeof(MediaQuery), }; 20 | } 21 | } 22 | 23 | public CssCompletionContext GetCompletionContext(ParseItem item, int position) 24 | { 25 | var token = item.StyleSheet.ItemBeforePosition(position); 26 | 27 | // Don't handle expressions. MediaFeatureContextProvider.cs does that 28 | if (token.FindType() != null) 29 | return null; 30 | 31 | return new CssCompletionContext((CssCompletionContextType)612, token.Start, token.Length, null); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Completion/Filter/HideInheritInitialCompletionListFilter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.TreeItems; 2 | using Microsoft.CSS.Editor.Completion; 3 | using Microsoft.VisualStudio.Utilities; 4 | using Microsoft.Web.Editor.Completion; 5 | using System.Collections.Generic; 6 | using System.ComponentModel.Composition; 7 | 8 | namespace CssTools 9 | { 10 | [Export(typeof(ICssCompletionListFilter))] 11 | [Name("Inherit/Initial Filter")] 12 | internal class HideInheritInitialCompletionListFilter : ICssCompletionListFilter 13 | { 14 | public void FilterCompletionList(IList completions, CssCompletionContext context) 15 | { 16 | if (context.ContextType != CssCompletionContextType.PropertyValue) 17 | return; 18 | 19 | // Only show inherit/initial/unset on the "all" property 20 | Declaration dec = context.ContextItem.FindType(); 21 | 22 | if (dec != null && dec.PropertyNameText == "all") 23 | return; 24 | 25 | foreach (CssCompletionEntry entry in completions) 26 | { 27 | if (entry.DisplayText == "initial" || entry.DisplayText == "inherit" || entry.DisplayText == "unset") 28 | { 29 | entry.FilterType = CompletionEntryFilterTypes.NeverVisible; 30 | } 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Completion/Filter/HideUncommonCompletionListFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Specialized; 3 | using System.ComponentModel.Composition; 4 | using Microsoft.VisualStudio.Utilities; 5 | using Microsoft.CSS.Editor.Completion; 6 | using Microsoft.Web.Editor.Completion; 7 | 8 | namespace CssTools 9 | { 10 | [Export(typeof(ICssCompletionListFilter))] 11 | [Name("HideUncommonCompletionListFilter")] 12 | internal class HideUncommonCompletionListFilter : ICssCompletionListFilter 13 | { 14 | private static readonly StringCollection _cache = new StringCollection() 15 | { 16 | "widows", // Rarely used and get's in the way of "width" 17 | }; 18 | 19 | public void FilterCompletionList(IList completions, CssCompletionContext context) 20 | { 21 | if (context.ContextType != CssCompletionContextType.PropertyName) 22 | return; 23 | 24 | foreach (CssCompletionEntry entry in completions) 25 | { 26 | if (_cache.Contains(entry.DisplayText)) 27 | { 28 | entry.FilterType = CompletionEntryFilterTypes.NeverVisible; 29 | } 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/Completion/Filter/ObsoleteCompletionListFilter.cs: -------------------------------------------------------------------------------- 1 |  2 | using Microsoft.CSS.Editor.Completion; 3 | using Microsoft.CSS.Editor.Schemas; 4 | using Microsoft.VisualStudio.Utilities; 5 | using Microsoft.Web.Editor.Completion; 6 | using System.Collections.Generic; 7 | using System.ComponentModel.Composition; 8 | 9 | namespace CssTools 10 | { 11 | [Export(typeof(ICssCompletionListFilter))] 12 | [Name("ObsoleteCompletionListFilter")] 13 | internal class ObsoleteCompletionListFilter : ICssCompletionListFilter 14 | { 15 | public void FilterCompletionList(IList completions, CssCompletionContext context) 16 | { 17 | ICssSchemaInstance rootSchema = CssSchemaManager.SchemaManager.GetSchemaRoot(null); 18 | ICssSchemaInstance schema = CssSchemaManager.SchemaManager.GetSchemaForItem(rootSchema, context.ContextItem); 19 | 20 | foreach (CssCompletionEntry entry in completions) 21 | { 22 | ICssCompletionListEntry prop = GetSchemaEntry(schema, context, entry); 23 | 24 | if (prop != null && !string.IsNullOrEmpty(prop.GetAttribute("obsolete"))) 25 | entry.FilterType = CompletionEntryFilterTypes.NeverVisible; 26 | } 27 | } 28 | 29 | private static ICssCompletionListEntry GetSchemaEntry(ICssSchemaInstance schema, CssCompletionContext context, CssCompletionEntry entry) 30 | { 31 | switch (context.ContextType) 32 | { 33 | case CssCompletionContextType.AtDirectiveName: 34 | return schema.GetAtDirective(entry.DisplayText); 35 | 36 | case CssCompletionContextType.PropertyName: 37 | return schema.GetProperty(entry.DisplayText); 38 | 39 | case CssCompletionContextType.PseudoClassOrElement: 40 | return schema.GetPseudo(entry.DisplayText); 41 | } 42 | 43 | return null; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/Completion/Filter/WebkitScrollbarCompletionListFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Specialized; 3 | using System.ComponentModel.Composition; 4 | using Microsoft.CSS.Core; 5 | using Microsoft.VisualStudio.Utilities; 6 | using Microsoft.CSS.Editor.Completion; 7 | using Microsoft.CSS.Core.Parser; 8 | using Microsoft.Web.Editor.Completion; 9 | 10 | namespace CssTools 11 | { 12 | [Export(typeof(ICssCompletionListFilter))] 13 | [Name("WebkitScrollbarCompletionListFilter")] 14 | internal class WebkitScrollbarCompletionListFilter : ICssCompletionListFilter 15 | { 16 | private static readonly StringCollection _cache = new StringCollection() 17 | { 18 | ":horizontal", 19 | ":vertical", 20 | ":decrement", 21 | ":increment", 22 | ":start", 23 | ":end", 24 | ":double-button", 25 | ":single-button", 26 | ":no-button", 27 | ":corner-present", 28 | ":window-inactive", 29 | }; 30 | 31 | public void FilterCompletionList(IList completions, CssCompletionContext context) 32 | { 33 | if (context.ContextType != CssCompletionContextType.PseudoClassOrElement) 34 | return; 35 | 36 | ParseItem prev = context.ContextItem.PreviousSibling; 37 | bool hasScrollbar = false; 38 | 39 | if (prev != null) 40 | { 41 | hasScrollbar = prev.Text.Contains(":-webkit-resizer") || prev.Text.Contains(":-webkit-scrollbar"); 42 | } 43 | 44 | foreach (CssCompletionEntry entry in completions) 45 | { 46 | if (hasScrollbar) 47 | { 48 | entry.FilterType = _cache.Contains(entry.DisplayText) ? entry.FilterType : CompletionEntryFilterTypes.NeverVisible; 49 | } 50 | else 51 | { 52 | entry.FilterType = !_cache.Contains(entry.DisplayText) ? entry.FilterType : CompletionEntryFilterTypes.NeverVisible; 53 | } 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/Completion/FontFamilyCompletionListEntry.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Editor.Completion; 2 | using Microsoft.CSS.Editor.Schemas.Browsers; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.Web.Editor.Completion; 5 | using System; 6 | 7 | namespace CssTools 8 | { 9 | /// 10 | /// This represents a font family in the completion list 11 | /// 12 | internal class FontFamilyCompletionListEntry : ICssCompletionListEntry 13 | { 14 | private string _name; 15 | 16 | public FontFamilyCompletionListEntry(string name) 17 | { 18 | _name = name ?? string.Empty; 19 | } 20 | 21 | public string DisplayText 22 | { 23 | get { return _name; } 24 | } 25 | 26 | public string Description 27 | { 28 | get { return string.Empty; } 29 | } 30 | 31 | public string GetSyntax(Version version) 32 | { 33 | return string.Empty; 34 | } 35 | 36 | public string GetAttribute(string name) 37 | { 38 | return string.Empty; 39 | } 40 | 41 | public string GetVersionedAttribute(string name, System.Version version) 42 | { 43 | return GetAttribute(name); 44 | } 45 | 46 | public string GetInsertionText(CssTextSource textSource, ITrackingSpan typingSpan) 47 | { 48 | string text = DisplayText; 49 | bool needsQuote = text.IndexOf(' ') != -1; 50 | if (text == "Pick from file...") 51 | { 52 | return string.Empty; 53 | } 54 | 55 | if (needsQuote) 56 | { 57 | // Prefer to use single quotes, but if the inline style uses single quotes, then use double quotes. 58 | char quote = (textSource == CssTextSource.InlineStyleSingleQuote) ? '"' : '\''; 59 | 60 | if (typingSpan != null) 61 | { 62 | // If the user already typed a quote, then use it 63 | 64 | string typingText = typingSpan.GetText(typingSpan.TextBuffer.CurrentSnapshot); 65 | 66 | if (!string.IsNullOrEmpty(typingText) && (typingText[0] == '"' || typingText[0] == '\'')) 67 | { 68 | quote = typingText[0]; 69 | } 70 | } 71 | 72 | if (text != null && text.IndexOf(quote) == -1) 73 | { 74 | text = quote.ToString() + text + quote.ToString(); 75 | } 76 | } 77 | 78 | return text; 79 | } 80 | 81 | public bool AllowQuotedString 82 | { 83 | get { return true; } 84 | } 85 | 86 | public bool IsBuilder 87 | { 88 | get { return false; } 89 | } 90 | 91 | public int SortingPriority 92 | { 93 | get { return 2; } 94 | } 95 | 96 | 97 | public bool IsSupported(BrowserVersion browser) 98 | { 99 | return true; 100 | } 101 | 102 | public bool IsSupported(Version cssVersion) 103 | { 104 | return true; 105 | } 106 | 107 | public ITrackingSpan ApplicableTo 108 | { 109 | get { return null; } 110 | } 111 | 112 | public CompletionEntryFilterTypes FilterType 113 | { 114 | get { return CompletionEntryFilterTypes.DefaultBuilder; } 115 | } 116 | 117 | public System.Windows.Media.ImageSource Icon 118 | { 119 | get { return null; } 120 | } 121 | 122 | public bool IsCommitChar(char typedCharacter) 123 | { 124 | return false; 125 | } 126 | 127 | public bool IsMuteCharacter(char typedCharacter) 128 | { 129 | return false; 130 | } 131 | 132 | public bool RetriggerIntellisense 133 | { 134 | get { return false; } 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /src/Completion/RegionCompletionListEntry.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Editor.Completion; 2 | using Microsoft.CSS.Editor.Schemas.Browsers; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.Web.Editor.Completion; 5 | using System; 6 | 7 | namespace CssTools 8 | { 9 | internal class RegionCompletionListEntry : ICssCompletionListEntry 10 | { 11 | public string Description 12 | { 13 | get { return string.Empty; } 14 | } 15 | 16 | public string DisplayText 17 | { 18 | get { return "Add region..."; } 19 | } 20 | 21 | public string GetSyntax(Version version) 22 | { 23 | return string.Empty; 24 | } 25 | 26 | public string GetAttribute(string name) 27 | { 28 | return string.Empty; 29 | } 30 | 31 | public string GetInsertionText(CssTextSource textSource, ITrackingSpan typingSpan) 32 | { 33 | return "region";//"/*#region MyRegion */\n\n\n\n/*#endregion*/"; 34 | } 35 | 36 | public string GetVersionedAttribute(string name, Version version) 37 | { 38 | return GetAttribute(name); 39 | } 40 | 41 | public bool AllowQuotedString 42 | { 43 | get { return false; } 44 | } 45 | 46 | public bool IsBuilder 47 | { 48 | get { return true; } 49 | } 50 | 51 | public int SortingPriority { get { return 0; } } 52 | 53 | 54 | public bool IsSupported(BrowserVersion browser) 55 | { 56 | return true; 57 | } 58 | 59 | public bool IsSupported(Version cssVersion) 60 | { 61 | return true; 62 | } 63 | 64 | 65 | public ITrackingSpan ApplicableTo 66 | { 67 | get { return null; } 68 | } 69 | 70 | public CompletionEntryFilterTypes FilterType 71 | { 72 | get { return CompletionEntryFilterTypes.AlwaysVisible; } 73 | } 74 | 75 | public System.Windows.Media.ImageSource Icon 76 | { 77 | get { return null; } 78 | } 79 | 80 | public bool IsCommitChar(char typedCharacter) 81 | { 82 | return false; 83 | } 84 | 85 | public bool IsMuteCharacter(char typedCharacter) 86 | { 87 | return false; 88 | } 89 | 90 | public bool RetriggerIntellisense 91 | { 92 | get { return false; } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/CssToolsPackage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using System; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace CssTools 6 | { 7 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 8 | [InstalledProductRegistration(Vsix.Name, Vsix.Description, Vsix.Version, IconResourceID = 400)] 9 | [Guid("8a85e089-0667-415f-a01c-f2f7564e7bac")] 10 | public sealed class CssToolsPackage : AsyncPackage 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/DragDrop/FontDrop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.Globalization; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Web; 8 | using System.Windows.Forms; 9 | using Microsoft.VisualStudio.Text.Editor; 10 | using Microsoft.VisualStudio.Text.Editor.DragDrop; 11 | using Microsoft.VisualStudio.Utilities; 12 | 13 | namespace CssTools 14 | { 15 | //[Export(typeof(IDropHandlerProvider))] // Updated 3 includes this functionality 16 | [DropFormat("FileDrop")] 17 | [DropFormat("CF_VSSTGPROJECTITEMS")] 18 | [Name("FontDropHandler")] 19 | [ContentType("CSS")] 20 | [ContentType("LESS")] 21 | [ContentType("SCSS")] 22 | [Order(Before = "DefaultFileDropHandler")] 23 | internal class FontDropHandlerProvider : IDropHandlerProvider 24 | { 25 | public IDropHandler GetAssociatedDropHandler(IWpfTextView view) 26 | { 27 | return view.Properties.GetOrCreateSingletonProperty(() => new FontDropHandler(view)); 28 | } 29 | } 30 | 31 | internal class FontDropHandler : IDropHandler 32 | { 33 | IWpfTextView view; 34 | private readonly Dictionary formats = new Dictionary() 35 | { 36 | {".ttf", " format('truetype')"}, 37 | {".woff", ""}, 38 | {".eot", ""}, 39 | {".otf", " format('opentype')"} 40 | }; 41 | private string draggedFilename; 42 | private string fontName = string.Empty; 43 | string fontFace = "@font-face {{\n\tfont-family: {0};\n\tsrc: {1};\n}}"; 44 | string fontUrls = "url('{0}'){1}"; 45 | 46 | public FontDropHandler(IWpfTextView view) 47 | { 48 | this.view = view; 49 | } 50 | 51 | public DragDropPointerEffects HandleDataDropped(DragDropInfo dragDropInfo) 52 | { 53 | if (File.Exists(draggedFilename)) 54 | { 55 | string fontFamily; 56 | view.TextBuffer.Insert(dragDropInfo.VirtualBufferPosition.Position.Position, GetCodeFromFile(draggedFilename, out fontFamily)); 57 | 58 | return DragDropPointerEffects.Copy; 59 | } 60 | else if (draggedFilename.StartsWith("http://localhost:", StringComparison.OrdinalIgnoreCase)) 61 | { 62 | view.TextBuffer.Insert(dragDropInfo.VirtualBufferPosition.Position.Position, GetCodeFromLocalhost()); 63 | 64 | return DragDropPointerEffects.Copy; 65 | } 66 | else 67 | return DragDropPointerEffects.None; 68 | } 69 | 70 | public string GetCodeFromFile(string fileName, out string fontFamily) 71 | { 72 | var files = GetRelativeFiles(fileName); 73 | string[] sources = new string[files.Count()]; 74 | 75 | for (int i = 0; i < files.Count(); i++) 76 | { 77 | string file = files.ElementAt(i); 78 | string extension = Path.GetExtension(file).ToLowerInvariant(); 79 | string reference = FileHelpers.RelativePath(ProjectHelpers.DTE.ActiveDocument.FullName, file); 80 | 81 | if (reference.StartsWith("http://localhost:", StringComparison.OrdinalIgnoreCase)) 82 | { 83 | int index = reference.IndexOf('/', 24); 84 | 85 | if (index > -1) 86 | reference = reference.Substring(index + 1).ToLowerInvariant(); 87 | } 88 | 89 | sources[i] = string.Format(CultureInfo.CurrentCulture, fontUrls, reference, formats[extension]); 90 | } 91 | 92 | string sourceUrls = string.Join(", ", sources); 93 | 94 | fontFamily = fontName; 95 | fontFamily = Uri.EscapeDataString(fontFamily); 96 | 97 | return string.Format(CultureInfo.CurrentCulture, fontFace, fontName, sourceUrls); 98 | } 99 | 100 | private string GetCodeFromLocalhost() 101 | { 102 | int index = draggedFilename.IndexOf('/', 24); 103 | 104 | if (index > -1) 105 | draggedFilename = draggedFilename.Substring(index).ToLowerInvariant(); 106 | 107 | string extension = Path.GetExtension(draggedFilename).ToLowerInvariant(); 108 | 109 | draggedFilename = Uri.EscapeDataString(draggedFilename); 110 | 111 | string sourceUrl = string.Format(CultureInfo.CurrentCulture, fontUrls, draggedFilename, formats[extension]); 112 | 113 | return string.Format(CultureInfo.CurrentCulture, fontFace, "MyFontName", sourceUrl); 114 | } 115 | 116 | private IEnumerable GetRelativeFiles(string fileName) 117 | { 118 | var fi = new FileInfo(fileName); 119 | 120 | fontName = fi.Name.Replace(fi.Extension, string.Empty); 121 | 122 | foreach (var file in fi.Directory.GetFiles(fontName + ".*")) 123 | { 124 | string extension = file.Extension.ToLowerInvariant(); 125 | 126 | if (formats.ContainsKey(extension)) 127 | yield return file.FullName; 128 | } 129 | } 130 | 131 | public void HandleDragCanceled() { } 132 | 133 | public DragDropPointerEffects HandleDragStarted(DragDropInfo dragDropInfo) 134 | { 135 | return DragDropPointerEffects.All; 136 | } 137 | 138 | public DragDropPointerEffects HandleDraggingOver(DragDropInfo dragDropInfo) 139 | { 140 | return DragDropPointerEffects.All; 141 | } 142 | 143 | public bool IsDropEnabled(DragDropInfo dragDropInfo) 144 | { 145 | draggedFilename = GetImageFilename(dragDropInfo); 146 | 147 | if (!string.IsNullOrEmpty(draggedFilename)) 148 | { 149 | string fileExtension = Path.GetExtension(draggedFilename).ToLowerInvariant(); 150 | 151 | if (this.formats.ContainsKey(fileExtension)) 152 | return true; 153 | } 154 | 155 | return false; 156 | } 157 | 158 | public static string GetImageFilename(DragDropInfo info) 159 | { 160 | DataObject data = new DataObject(info.Data); 161 | 162 | if (info.Data.GetDataPresent("FileDrop")) 163 | { 164 | // The drag and drop operation came from the file system 165 | StringCollection files = data.GetFileDropList(); 166 | 167 | if (files != null && files.Count == 1) 168 | return files[0]; 169 | } 170 | else if (info.Data.GetDataPresent("CF_VSSTGPROJECTITEMS")) 171 | return data.GetText(); // The drag and drop operation came from the VS solution explorer 172 | else if (info.Data.GetDataPresent("MultiURL")) 173 | return data.GetText(); 174 | 175 | return null; 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/ExtensionMethods/AtDirectiveExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.CSS.Core; 5 | using Microsoft.CSS.Editor.Schemas; 6 | using Microsoft.CSS.Core.TreeItems.AtDirectives; 7 | using Microsoft.CSS.Editor.Completion; 8 | 9 | namespace CssTools 10 | { 11 | internal static class AtDirectiveExtensions 12 | { 13 | public static bool IsVendorSpecific(this AtDirective directive) 14 | { 15 | return directive.Keyword.Text[0] == '-'; 16 | } 17 | 18 | 19 | public static bool TryGetStandardPropertyName(this AtDirective directive, out string standardName, ICssSchemaInstance schema) 20 | { 21 | standardName = null; 22 | 23 | string propText = directive.Keyword.Text; 24 | string prefix = VendorHelpers.GetPrefixes(schema).SingleOrDefault(p => propText.StartsWith(p, StringComparison.Ordinal)); 25 | if (prefix != null) 26 | { 27 | standardName = propText.Substring(prefix.Length); 28 | return true; 29 | } 30 | 31 | return false; 32 | } 33 | 34 | public static IEnumerable GetMissingVendorSpecifics(this AtDirective directive, ICssSchemaInstance schema) 35 | { 36 | IEnumerable possible = GetPossibleVendorSpecifics(directive, schema); 37 | 38 | var visitorRules = new CssItemCollector(); 39 | directive.Parent.Accept(visitorRules); 40 | 41 | foreach (string item in possible) 42 | { 43 | if (!visitorRules.Items.Any(d => d.Keyword != null && "@" + d.Keyword.Text == item)) 44 | yield return item; 45 | } 46 | } 47 | 48 | public static IEnumerable GetPossibleVendorSpecifics(this AtDirective directive, ICssSchemaInstance schema) 49 | { 50 | string text = directive.Keyword.Text; 51 | 52 | foreach (string prefix in VendorHelpers.GetPrefixes(schema).Where(p => p != "-o-")) // Remove -o- since the parser doesn't recognize -o-keyframes 53 | { 54 | ICssCompletionListEntry entry = schema.GetAtDirective("@" + prefix + text); 55 | if (entry != null && string.IsNullOrEmpty(entry.GetAttribute("obsolete"))) 56 | yield return entry.DisplayText; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/ExtensionMethods/ColorModelExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Web.Editor.ColorPicker; 2 | using System.Windows.Media; 3 | 4 | namespace CssTools 5 | { 6 | internal static class ColorModelExtensions 7 | { 8 | private const float _factor = 0.025F; 9 | 10 | public static ColorModel Brighten(this ColorModel color) 11 | { 12 | if ((color.HslLightness + _factor) < 1) 13 | { 14 | color.HslLightness += _factor; 15 | } 16 | 17 | return color; 18 | } 19 | 20 | public static ColorModel Darken(this ColorModel color) 21 | { 22 | if ((color.HslLightness - _factor) > 0) 23 | { 24 | color.HslLightness -= _factor; 25 | } 26 | 27 | return color; 28 | } 29 | 30 | public static ColorModel Invert(this ColorModel color) 31 | { 32 | ColorModel model = new ColorModel() 33 | { 34 | Red = ~(byte)color.Red, 35 | Green = ~(byte)color.Green, 36 | Blue = ~(byte)color.Blue 37 | }; 38 | 39 | return model; 40 | 41 | } 42 | 43 | public static SolidColorBrush ToBrush(this ColorModel color) 44 | { 45 | Color c = Color.FromRgb( 46 | (byte)color.Red, 47 | (byte)color.Green, 48 | (byte)color.Blue 49 | ); 50 | 51 | SolidColorBrush brush = new SolidColorBrush(c); 52 | brush.Freeze(); 53 | return brush; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/ExtensionMethods/CssExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Microsoft.CSS.Core; 4 | using Microsoft.CSS.Editor.Schemas; 5 | using Microsoft.CSS.Core.TreeItems.Functions; 6 | using Microsoft.CSS.Core.TreeItems.Selectors; 7 | using Microsoft.CSS.Core.Parser; 8 | using Microsoft.CSS.Core.Checker; 9 | using Microsoft.CSS.Core.TreeItems; 10 | using System.Collections.Generic; 11 | 12 | namespace CssTools 13 | { 14 | public static class CssExtensions 15 | { 16 | ///Gets the selector portion of the text of a Selector object, excluding any trailing comma. 17 | public static string SelectorText(this Selector selector) 18 | { 19 | if (selector.Comma == null) return selector.Text; 20 | return selector.Text.Substring(0, selector.Comma.Start - selector.Start).Trim(); 21 | } 22 | 23 | public static bool IsPseudoElement(this ParseItem item) 24 | { 25 | if (item.Text.StartsWith("::", StringComparison.Ordinal)) 26 | return true; 27 | 28 | var schema = CssSchemaManager.SchemaManager.GetSchemaRoot(null); 29 | return schema.GetPseudo(":" + item.Text) != null; 30 | } 31 | 32 | public static bool IsDataUri(this UrlItem item) 33 | { 34 | if (item.UrlString == null || string.IsNullOrEmpty(item.UrlString.Text)) 35 | return false; 36 | 37 | return item.UrlString.Text.Contains(";base64,"); 38 | } 39 | 40 | /// 41 | /// Use this to make things work on 15.4 and 15.5+ 42 | /// 43 | public static IEnumerable GetDeclarations(this RuleBlock rule) 44 | { 45 | var declaration = rule.GetType().GetProperty("Declarations")?.GetValue(rule); 46 | if (declaration == null) 47 | return System.Linq.Enumerable.Empty(); 48 | else 49 | return declaration as IEnumerable; 50 | } 51 | 52 | //[SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Justification = "Match enum name")] 53 | //public static CssErrorFlags ToCssErrorFlags(this WarningLocation location) 54 | //{ 55 | // switch (location) 56 | // { 57 | // case WarningLocation.Warnings: 58 | // return CssErrorFlags.UnderlinePurple | CssErrorFlags.TaskListWarning; 59 | 60 | // default: 61 | // return CssErrorFlags.UnderlinePurple | CssErrorFlags.TaskListMessage; 62 | // } 63 | //} 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/ExtensionMethods/DeclarationExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.CSS.Core; 5 | using Microsoft.CSS.Editor.Schemas; 6 | using Microsoft.CSS.Core.TreeItems; 7 | using Microsoft.CSS.Editor.Completion; 8 | 9 | namespace CssTools 10 | { 11 | internal static class DeclarationExtensions 12 | { 13 | public static bool IsVendorSpecific(this Declaration declaration) 14 | { 15 | return declaration.PropertyName.Text.StartsWith("-", StringComparison.Ordinal); 16 | } 17 | 18 | public static bool TryGetStandardPropertyName(this Declaration declaration, out string standardName, ICssSchemaInstance schema) 19 | { 20 | standardName = null; 21 | 22 | if (declaration.IsVendorSpecific()) 23 | { 24 | string propText = declaration.PropertyName.Text; 25 | string prefix = VendorHelpers.GetPrefixes(schema).SingleOrDefault(p => propText.StartsWith(p, StringComparison.Ordinal)); 26 | if (prefix != null) 27 | { 28 | standardName = propText.Substring(prefix.Length); 29 | return true; 30 | } 31 | } 32 | 33 | return false; 34 | } 35 | 36 | public static IEnumerable GetMissingVendorSpecifics(this Declaration declaration, ICssSchemaInstance schema) 37 | { 38 | RuleBlock rule = declaration.FindType(); 39 | IEnumerable possible = GetPossibleVendorSpecifics(declaration, schema); 40 | 41 | foreach (string item in possible) 42 | { 43 | if (!rule.GetDeclarations().Any(d => d.PropertyName != null && d.PropertyName.Text == item)) 44 | yield return item; 45 | } 46 | } 47 | 48 | public static IEnumerable GetPossibleVendorSpecifics(this Declaration declaration, ICssSchemaInstance schema) 49 | { 50 | string text = declaration.PropertyName.Text; 51 | 52 | foreach (string prefix in VendorHelpers.GetPrefixes(schema)) 53 | { 54 | ICssCompletionListEntry entry = schema.GetProperty(prefix + text); 55 | if (entry != null && string.IsNullOrEmpty(entry.GetAttribute("obsolete"))) 56 | yield return entry.DisplayText; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/ExtensionMethods/Extensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Text; 2 | using System; 3 | using System.Linq; 4 | using System.Windows.Media.Imaging; 5 | 6 | namespace CssTools 7 | { 8 | static class Extensions 9 | { 10 | ///Runs a callback when an iamge is fully downloaded, or immediately if the image has already been downloaded. 11 | public static void OnDownloaded(this BitmapSource image, Action callback) 12 | { 13 | if (image.IsDownloading) 14 | image.DownloadCompleted += (s, e) => callback(); 15 | else 16 | callback(); 17 | } 18 | 19 | ///Replaces a TextBuffer's entire content with the specified text. 20 | public static void SetText(this ITextBuffer buffer, string text) 21 | { 22 | buffer.Replace(new Span(0, buffer.CurrentSnapshot.Length), text); 23 | } 24 | 25 | ///Test the numericality of sequence. 26 | public static bool IsNumeric(this string input) 27 | { 28 | return input.All(digit => char.IsDigit(digit) || digit.Equals('.')); 29 | } 30 | 31 | ///Find the cloumn position in the last line. 32 | public static int GetLineColumn(this string text, int targetIndex, int lineNumber) 33 | { 34 | var result = targetIndex - text.NthIndexOfCharInString('\n', lineNumber); 35 | 36 | return Math.Max(0, result); 37 | } 38 | 39 | //Find the nth occurance of needle in haystack.. 40 | public static int NthIndexOfCharInString(this string strHaystack, char charNeedle, int intOccurrenceToFind) 41 | { 42 | if (intOccurrenceToFind < 1) return 0; 43 | 44 | int intReturn = -1; 45 | int count = 0; 46 | int n = 0; 47 | 48 | while (count < intOccurrenceToFind && (n = strHaystack.IndexOf(charNeedle, n)) != -1) 49 | { 50 | n++; 51 | count++; 52 | } 53 | 54 | if (count == intOccurrenceToFind) intReturn = n; 55 | 56 | return intReturn; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Helpers/CssItemAggregator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Diagnostics.CodeAnalysis; 5 | using Microsoft.CSS.Core; 6 | using Microsoft.CSS.Core.Parser; 7 | 8 | namespace CssTools 9 | { 10 | /// 11 | /// Crawls a CSS parse tree, gathering node of various types into a uniform collection. 12 | /// This class allows you to gather multiple types of ParseItems without recrawling the 13 | /// tree for each type. 14 | /// To use this class, add a collection initializer with a list of typed lambdas. 15 | /// 16 | [SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface", Justification = "Not actually a collection; implements IEnumerable for initializer syntax")] 17 | [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] 18 | public sealed class CssItemAggregator : ICssSimpleTreeVisitor, System.Collections.IEnumerable // For collection initializer syntax 19 | { 20 | public ReadOnlyCollection Items { get; private set; } 21 | private readonly bool _includeChildren; 22 | private readonly List _writableItems = new List(); 23 | private readonly List> _funcs = new List>(); 24 | private readonly Func _filter; 25 | 26 | public CssItemAggregator() : this(false) { } 27 | 28 | public CssItemAggregator(Func filter) 29 | : this(false) 30 | { 31 | _filter = filter; 32 | } 33 | 34 | public CssItemAggregator(bool includeChildren) 35 | { 36 | _includeChildren = includeChildren; 37 | Items = new ReadOnlyCollection(_writableItems); 38 | } 39 | 40 | public void Add(Func selector) where TNode : ParseItem 41 | { 42 | _funcs.Add(item => 43 | { 44 | var typedItem = item as TNode; 45 | if (typedItem == null) 46 | return false; 47 | _writableItems.Add(selector(typedItem)); 48 | return true; 49 | }); 50 | } 51 | 52 | [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "multi")] 53 | public void Add(Func> multiSelector) where TNode : ParseItem 54 | { 55 | _funcs.Add(item => 56 | { 57 | var typedItem = item as TNode; 58 | if (typedItem == null) 59 | return false; 60 | _writableItems.AddRange(multiSelector(typedItem)); 61 | return true; 62 | }); 63 | } 64 | 65 | VisitItemResult ICssSimpleTreeVisitor.Visit(ParseItem parseItem) 66 | { 67 | if (_filter != null && !_filter(parseItem)) 68 | return VisitItemResult.SkipChildren; 69 | 70 | foreach (var func in _funcs) 71 | { 72 | if (!func(parseItem)) 73 | continue; 74 | if (!_includeChildren) 75 | return VisitItemResult.SkipChildren; 76 | } 77 | return VisitItemResult.Continue; 78 | } 79 | 80 | public ReadOnlyCollection Crawl(ParseItem root) 81 | { 82 | root.Accept(this); 83 | return Items; 84 | } 85 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 86 | { 87 | throw new System.NotImplementedException("Not actually a collection"); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Helpers/CssItemCollector.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.CSS.Core; 3 | using Microsoft.CSS.Core.Parser; 4 | 5 | namespace CssTools 6 | { 7 | /// 8 | /// Creates a list of CSS ParseItems of a certain type 9 | /// (when passed in as the visitor to any item's Accept() function) 10 | /// 11 | internal class CssItemCollector : ICssSimpleTreeVisitor where T : ParseItem 12 | { 13 | public IList Items { get; private set; } 14 | private bool _includeChildren; 15 | 16 | public CssItemCollector() : this(false) { } 17 | 18 | public CssItemCollector(bool includeChildren) 19 | { 20 | _includeChildren = includeChildren; 21 | Items = new List(); 22 | } 23 | 24 | public VisitItemResult Visit(ParseItem parseItem) 25 | { 26 | var item = parseItem as T; 27 | 28 | if (item != null) 29 | { 30 | Items.Add(item); 31 | return (_includeChildren) ? VisitItemResult.Continue : VisitItemResult.SkipChildren; 32 | } 33 | 34 | return VisitItemResult.Continue; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Helpers/CssTreeWatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.ComponentModel.Composition; 4 | using System.Linq; 5 | using Microsoft.CSS.Core; 6 | using Microsoft.CSS.Editor; 7 | using Microsoft.VisualStudio.Text; 8 | using Microsoft.VisualStudio.Text.Editor; 9 | using Microsoft.VisualStudio.Utilities; 10 | using Microsoft.CSS.Core.Parser; 11 | using Microsoft.CSS.Core.Document; 12 | using Microsoft.CSS.Editor.Document; 13 | 14 | namespace CssTools 15 | { 16 | [TextViewRole(PredefinedTextViewRoles.Editable)] 17 | [ContentType("css")] 18 | [Order(After = "Visual Studio CSS Text View Connection Listener")] 19 | [Export(typeof(IWpfTextViewConnectionListener))] 20 | class CssTreeWatcherAdder : IWpfTextViewConnectionListener 21 | { 22 | public void SubjectBuffersConnected(IWpfTextView textView, ConnectionReason reason, Collection subjectBuffers) 23 | { 24 | foreach (var buffer in subjectBuffers.Where(b => b.ContentType.IsOfType("css"))) 25 | { 26 | CssTreeWatcher watcher; 27 | if (buffer.Properties.TryGetProperty(typeof(CssTreeWatcher), out watcher)) 28 | watcher.Tree = CssEditorDocument.FromTextBuffer(buffer).Tree; 29 | } 30 | } 31 | 32 | public void SubjectBuffersDisconnected(IWpfTextView textView, ConnectionReason reason, Collection subjectBuffers) 33 | { 34 | foreach (var buffer in subjectBuffers.Where(b => b.ContentType.IsOfType("css"))) 35 | { 36 | CssTreeWatcher watcher; 37 | if (buffer.Properties.TryGetProperty(typeof(CssTreeWatcher), out watcher)) 38 | watcher.Tree = null; 39 | } 40 | } 41 | } 42 | ///A persistent wrapper around a CssTree for a single TextBuffer 43 | /// A ProjectionBuffer's CssTree can be replaced when the buffer 44 | /// disconnected during a Format Document operation. This wrapper detects 45 | /// this and forwards events from the new CssTree. 46 | /// When using this class, you must only add event handlers to the events 47 | /// in the wrapper class, not those in CssTree. 48 | public class CssTreeWatcher 49 | { 50 | private CssTree _tree; 51 | 52 | public ITextBuffer Buffer { get; private set; } 53 | 54 | public CssTree Tree 55 | { 56 | get { return _tree; } 57 | 58 | set 59 | { 60 | if (Tree == value) return; 61 | if (Tree != null) 62 | { 63 | Tree.ItemsChanged -= Tree_ItemsChanged; 64 | Tree.TreeUpdated -= Tree_TreeUpdated; 65 | } 66 | _tree = value; 67 | if (value != null) 68 | { 69 | Tree.ItemsChanged += Tree_ItemsChanged; 70 | Tree.TreeUpdated += Tree_TreeUpdated; 71 | } 72 | } 73 | } 74 | 75 | public ParseItem StyleSheet { get { return Tree == null ? null : Tree.StyleSheet; } } 76 | 77 | private void Tree_TreeUpdated(object sender, CssTreeUpdateEventArgs e) { if (TreeUpdated != null) TreeUpdated(sender, e); } 78 | private void Tree_ItemsChanged(object sender, CssItemsChangedEventArgs e) { if (ItemsChanged != null) ItemsChanged(sender, e); } 79 | 80 | private CssTreeWatcher(ITextBuffer buffer) 81 | { 82 | Buffer = buffer; 83 | Tree = CssEditorDocument.FromTextBuffer(buffer).Tree; 84 | } 85 | public static CssTreeWatcher ForBuffer(ITextBuffer buffer) 86 | { 87 | return buffer.Properties.GetOrCreateSingletonProperty(() => new CssTreeWatcher(buffer)); 88 | } 89 | 90 | public event EventHandler ItemsChanged; 91 | public event EventHandler TreeUpdated; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Helpers/OptionHelpers.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using Microsoft.VisualStudio.Shell.Interop; 3 | using Microsoft.Web.Editor.ColorPicker; 4 | using System; 5 | using System.Diagnostics.CodeAnalysis; 6 | using VS = Microsoft.VisualStudio; 7 | 8 | namespace CssTools 9 | { 10 | internal static class OptionHelpers 11 | { 12 | private static int _fontSize; 13 | private static ColorModel _backgroundColor; 14 | private static object _syncRoot = new object(); 15 | 16 | // TODO: Compensate for the current line highlighting 17 | public static ColorModel BackgroundColor 18 | { 19 | get 20 | { 21 | if (_backgroundColor == null) 22 | { 23 | lock (_syncRoot) 24 | { 25 | if (_backgroundColor == null) 26 | { 27 | GetSize(); 28 | } 29 | } 30 | } 31 | 32 | return _backgroundColor; 33 | } 34 | } 35 | 36 | public static int FontSize 37 | { 38 | get 39 | { 40 | if (_fontSize == 0) 41 | { 42 | lock (_syncRoot) 43 | { 44 | if (_fontSize == 0) 45 | { 46 | GetSize(); 47 | } 48 | } 49 | } 50 | 51 | return _fontSize; 52 | } 53 | } 54 | 55 | [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults")] 56 | private static void GetSize() 57 | { 58 | try 59 | { 60 | IVsFontAndColorStorage storage = (IVsFontAndColorStorage)Package.GetGlobalService(typeof(IVsFontAndColorStorage)); 61 | var guid = new Guid("A27B4E24-A735-4d1d-B8E7-9716E1E3D8E0"); 62 | if (storage != null && storage.OpenCategory(ref guid, (uint)(__FCSTORAGEFLAGS.FCSF_READONLY | __FCSTORAGEFLAGS.FCSF_LOADDEFAULTS)) == VS.VSConstants.S_OK) 63 | { 64 | LOGFONTW[] Fnt = new LOGFONTW[] { new LOGFONTW() }; 65 | FontInfo[] Info = new FontInfo[] { new FontInfo() }; 66 | storage.GetFont(Fnt, Info); 67 | _fontSize = (int)Info[0].wPointSize; 68 | } 69 | 70 | if (storage != null && storage.OpenCategory(ref guid, (uint)(__FCSTORAGEFLAGS.FCSF_NOAUTOCOLORS | __FCSTORAGEFLAGS.FCSF_LOADDEFAULTS)) == VS.VSConstants.S_OK) 71 | { 72 | var info = new ColorableItemInfo[1]; 73 | storage.GetItem("Plain Text", info); 74 | _backgroundColor = ConvertFromWin32Color((int)info[0].crBackground); 75 | } 76 | 77 | } 78 | catch { } 79 | } 80 | 81 | public static ColorModel ConvertFromWin32Color(int color) 82 | { 83 | int r = color & 0x000000FF; 84 | int g = (color & 0x0000FF00) >> 8; 85 | int b = (color & 0x00FF0000) >> 16; 86 | return new ColorModel() { Red = r, Green = g, Blue = b }; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Helpers/VendorHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.CSS.Core; 5 | using Microsoft.CSS.Editor.Schemas; 6 | using Microsoft.CSS.Editor.Completion; 7 | using Microsoft.CSS.Core.TreeItems; 8 | using Microsoft.CSS.Editor.Checker; 9 | using Microsoft.CSS.Core.TreeItems.AtDirectives; 10 | using Microsoft.CSS.Core.Checker; 11 | 12 | namespace CssTools 13 | { 14 | internal static class VendorHelpers 15 | { 16 | private static Dictionary prefixes = new Dictionary(); 17 | 18 | public static string[] GetPrefixes(ICssSchemaInstance schema) 19 | { 20 | int version = schema.Version.GetHashCode(); 21 | int browser = schema.Filter.Name.GetHashCode(); 22 | int hash = version ^ browser; 23 | 24 | if (!prefixes.ContainsKey(hash)) 25 | { 26 | CssSchemaManager.SchemaManager.CurrentSchemaChanged += CurrentSchemaChanged; 27 | var properties = schema.Properties; 28 | List list = new List(); 29 | 30 | foreach (ICssCompletionListEntry property in properties) 31 | { 32 | string text = property.DisplayText; 33 | if (text[0] == '-') 34 | { 35 | int end = text.IndexOf('-', 1); 36 | if (end > -1) 37 | { 38 | string prefix = text.Substring(0, end + 1); 39 | if (!list.Contains(prefix)) 40 | { 41 | list.Add(prefix); 42 | } 43 | } 44 | } 45 | } 46 | 47 | prefixes.Add(hash, list.ToArray()); 48 | } 49 | 50 | return prefixes[hash]; 51 | } 52 | 53 | private static void CurrentSchemaChanged(object sender, EventArgs e) 54 | { 55 | CssSchemaManager.SchemaManager.CurrentSchemaChanged -= CurrentSchemaChanged; 56 | } 57 | 58 | public static bool HasVendorLaterInRule(Declaration declaration, ICssSchemaInstance schema) 59 | { 60 | Declaration next = declaration.NextSibling as Declaration; 61 | 62 | while (next != null) 63 | { 64 | if (next.IsValid && next.IsVendorSpecific()) 65 | { 66 | foreach (string prefix in GetPrefixes(schema)) 67 | { 68 | if (next.PropertyName.Text == prefix + declaration.PropertyName.Text) 69 | return true; 70 | } 71 | } 72 | 73 | next = next.NextSibling as Declaration; 74 | } 75 | 76 | return false; 77 | } 78 | 79 | public static IEnumerable GetMatchingVendorEntriesInRule(Declaration declaration, RuleBlock rule, ICssSchemaInstance schema) 80 | { 81 | foreach (Declaration d in rule.GetDeclarations().Where(d => d.IsValid && d.IsVendorSpecific())) 82 | foreach (string prefix in GetPrefixes(schema)) 83 | { 84 | if (d.PropertyName.Text == prefix + declaration.PropertyName.Text) 85 | { 86 | yield return d; 87 | break; 88 | } 89 | } 90 | } 91 | 92 | public static ICssCompletionListEntry GetMatchingStandardEntry(Declaration declaration, ICssSchemaInstance rootSchema) 93 | { 94 | string standardName; 95 | if (declaration.TryGetStandardPropertyName(out standardName, rootSchema)) 96 | { 97 | ICssSchemaInstance schema = CssSchemaManager.SchemaManager.GetSchemaForItem(rootSchema, declaration); 98 | return schema.GetProperty(standardName); 99 | } 100 | 101 | return null; 102 | } 103 | 104 | public static ICssCompletionListEntry GetMatchingStandardEntry(Declaration declaration, ICssCheckerContext context) 105 | { 106 | string standardName; 107 | if (declaration.TryGetStandardPropertyName(out standardName, CssEditorChecker.GetSchemaForItem(context, declaration))) 108 | { 109 | ICssSchemaInstance schema = CssEditorChecker.GetSchemaForItem(context, declaration); 110 | return schema.GetProperty(standardName); 111 | } 112 | 113 | return null; 114 | } 115 | 116 | public static ICssCompletionListEntry GetMatchingStandardEntry(AtDirective directive, ICssCheckerContext context) 117 | { 118 | string standardName; 119 | if (directive.TryGetStandardPropertyName(out standardName, CssEditorChecker.GetSchemaForItem(context, directive))) 120 | { 121 | ICssSchemaInstance schema = CssEditorChecker.GetSchemaForItem(context, directive); 122 | return schema.GetAtDirective("@" + standardName); 123 | } 124 | 125 | return null; 126 | } 127 | 128 | public static ICssCompletionListEntry GetMatchingStandardEntry(AtDirective directive, ICssSchemaInstance rootSchema) 129 | { 130 | string standardName; 131 | if (directive.TryGetStandardPropertyName(out standardName, rootSchema)) 132 | { 133 | ICssSchemaInstance schema = CssSchemaManager.SchemaManager.GetSchemaForItem(rootSchema, directive); 134 | return schema.GetAtDirective("@" + standardName); 135 | } 136 | 137 | return null; 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using CssTools; 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 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/Font/FontQuickInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing.Text; 4 | using System.Linq; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Media; 8 | using Microsoft.CSS.Core; 9 | using Microsoft.CSS.Editor; 10 | using Microsoft.VisualStudio.Language.Intellisense; 11 | using Microsoft.VisualStudio.Text; 12 | using Microsoft.CSS.Core.Parser; 13 | using Microsoft.CSS.Editor.Document; 14 | using Microsoft.CSS.Core.TreeItems; 15 | 16 | namespace CssTools 17 | { 18 | internal class FontQuickInfo : IQuickInfoSource 19 | { 20 | private ITextBuffer _buffer; 21 | private static InstalledFontCollection fonts = new InstalledFontCollection(); 22 | private List _allowed = new List() { "FONT", "FONT-FAMILY" }; 23 | 24 | public FontQuickInfo(ITextBuffer subjectBuffer) 25 | { 26 | _buffer = subjectBuffer; 27 | } 28 | 29 | public void AugmentQuickInfoSession(IQuickInfoSession session, IList qiContent, out ITrackingSpan applicableToSpan) 30 | { 31 | applicableToSpan = null; 32 | 33 | if (session == null || qiContent == null) 34 | return; 35 | 36 | // Map the trigger point down to our buffer. 37 | SnapshotPoint? point = session.GetTriggerPoint(_buffer.CurrentSnapshot); 38 | if (!point.HasValue) 39 | return; 40 | 41 | var tree = CssEditorDocument.FromTextBuffer(_buffer); 42 | ParseItem item = tree.StyleSheet.ItemBeforePosition(point.Value.Position); 43 | if (item == null || !item.IsValid) 44 | return; 45 | 46 | Declaration dec = item.FindType(); 47 | if (dec == null || !dec.IsValid || !_allowed.Contains(dec.PropertyName.Text.ToUpperInvariant())) 48 | return; 49 | 50 | string fontName = item.Text.Trim('\'', '"'); 51 | 52 | if (fonts.Families.SingleOrDefault(f => f.Name.Equals(fontName, StringComparison.OrdinalIgnoreCase)) != null) 53 | { 54 | FontFamily font = new FontFamily(fontName); 55 | 56 | applicableToSpan = _buffer.CurrentSnapshot.CreateTrackingSpan(item.Start, item.Length, SpanTrackingMode.EdgeNegative); 57 | qiContent.Add(CreateFontPreview(font, 10)); 58 | qiContent.Add(CreateFontPreview(font, 11)); 59 | qiContent.Add(CreateFontPreview(font, 12)); 60 | qiContent.Add(CreateFontPreview(font, 14)); 61 | qiContent.Add(CreateFontPreview(font, 25)); 62 | qiContent.Add(CreateFontPreview(font, 40)); 63 | } 64 | } 65 | 66 | private static UIElement CreateFontPreview(FontFamily font, double size) 67 | { 68 | return new TextBlock() 69 | { 70 | Text = font.Source + " (" + size + "px)", 71 | FontFamily = font, 72 | FontSize = size, 73 | }; 74 | } 75 | 76 | private bool m_isDisposed; 77 | public void Dispose() 78 | { 79 | if (!m_isDisposed) 80 | { 81 | GC.SuppressFinalize(this); 82 | m_isDisposed = true; 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/QuickInfo/Font/FontQuickInfoController.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 CssTools 7 | { 8 | internal class FontQuickInfoController : IIntellisenseController 9 | { 10 | private ITextView m_textView; 11 | private IList m_subjectBuffers; 12 | private FontQuickInfoControllerProvider m_provider; 13 | 14 | internal FontQuickInfoController(ITextView textView, IList subjectBuffers, FontQuickInfoControllerProvider 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 | public void ConnectSubjectBuffer(ITextBuffer subjectBuffer) 54 | { 55 | } 56 | 57 | public void DisconnectSubjectBuffer(ITextBuffer subjectBuffer) 58 | { 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/QuickInfo/Font/FontQuickInfoControllerProvider.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 CssTools 9 | { 10 | [Export(typeof(IIntellisenseControllerProvider))] 11 | [Name("Font QuickInfo Controller")] 12 | [ContentType("css")] 13 | public class FontQuickInfoControllerProvider : IIntellisenseControllerProvider 14 | { 15 | [Import] 16 | public IQuickInfoBroker QuickInfoBroker { get; set; } 17 | 18 | public IIntellisenseController TryCreateIntellisenseController(ITextView textView, IList subjectBuffers) 19 | { 20 | return new FontQuickInfoController(textView, subjectBuffers, this); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/QuickInfo/Font/FontQuickInfoSourceProvider.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Language.Intellisense; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Utilities; 5 | 6 | namespace CssTools 7 | { 8 | [Export(typeof(IQuickInfoSourceProvider))] 9 | [Name("Font QuickInfo Source")] 10 | [Order(Before = "Default Quick Info Presenter")] 11 | [ContentType("CSS")] 12 | internal class FontQuickInfoSourceProvider : IQuickInfoSourceProvider 13 | { 14 | public IQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer) 15 | { 16 | return new FontQuickInfo(textBuffer); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/QuickInfo/Image/ImageQuickInfo.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.Document; 2 | using Microsoft.CSS.Core.Parser; 3 | using Microsoft.CSS.Core.TreeItems.Functions; 4 | using Microsoft.CSS.Editor.Document; 5 | using Microsoft.VisualStudio.Language.Intellisense; 6 | using Microsoft.VisualStudio.Text; 7 | using Microsoft.Web.Editor.EditorHelpers; 8 | using Microsoft.Web.Editor.Host; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Drawing; 12 | using System.IO; 13 | using System.Windows; 14 | using System.Windows.Controls; 15 | using System.Windows.Forms; 16 | using System.Windows.Media; 17 | using System.Windows.Media.Imaging; 18 | using Svg; 19 | using Image = System.Windows.Controls.Image; 20 | 21 | namespace CssTools 22 | { 23 | internal class ImageQuickInfo : IQuickInfoSource 24 | { 25 | private ITextBuffer _buffer; 26 | private CssTree _tree; 27 | 28 | public ImageQuickInfo(ITextBuffer subjectBuffer) 29 | { 30 | _buffer = subjectBuffer; 31 | } 32 | 33 | public void AugmentQuickInfoSession(IQuickInfoSession session, IList qiContent, out ITrackingSpan applicableToSpan) 34 | { 35 | applicableToSpan = null; 36 | 37 | if (!EnsureTreeInitialized() || session == null || qiContent == null) 38 | return; 39 | 40 | SnapshotPoint? point = session.GetTriggerPoint(_buffer.CurrentSnapshot); 41 | if (!point.HasValue) 42 | return; 43 | 44 | ParseItem item = _tree.StyleSheet.ItemBeforePosition(point.Value.Position); 45 | if (item == null || !item.IsValid) 46 | return; 47 | 48 | UrlItem urlItem = item.FindType(); 49 | 50 | if (urlItem == null || urlItem.UrlString == null || !urlItem.UrlString.IsValid) 51 | return; 52 | 53 | string url = GetFullUrl(urlItem.UrlString.Text.Trim('\'', '"'), _buffer); 54 | if (string.IsNullOrEmpty(url)) 55 | return; 56 | 57 | applicableToSpan = _buffer.CurrentSnapshot.CreateTrackingSpan(point.Value.Position, 1, SpanTrackingMode.EdgeNegative); 58 | 59 | AddImageContent(qiContent, url); 60 | } 61 | 62 | /// 63 | /// This must be delayed so that the TextViewConnectionListener 64 | /// has a chance to initialize the WebEditor host. 65 | /// 66 | public bool EnsureTreeInitialized() 67 | { 68 | if (_tree == null)// && WebEditor.GetHost(CssContentTypeDefinition.CssContentType) != null) 69 | { 70 | try 71 | { 72 | CssEditorDocument document = CssEditorDocument.FromTextBuffer(_buffer); 73 | _tree = document.Tree; 74 | } 75 | catch (Exception) 76 | { 77 | } 78 | } 79 | 80 | return _tree != null; 81 | } 82 | 83 | public static string GetFullUrl(string text, ITextBuffer sourceBuffer) 84 | { 85 | return GetFullUrl(text, sourceBuffer.GetFileName() ?? ProjectHelpers.DTE.ActiveDocument.FullName); 86 | } 87 | public static string GetFullUrl(string text, string sourceFilename) 88 | { 89 | if (string.IsNullOrEmpty(text)) 90 | return null; 91 | 92 | text = text.Trim(new[] { '\'', '"', '~' }); 93 | 94 | if (text.StartsWith("//", StringComparison.Ordinal)) 95 | text = "http:" + text; 96 | 97 | if (text.Contains("://") || text.StartsWith("data:", StringComparison.Ordinal)) 98 | return text; 99 | 100 | if (String.IsNullOrEmpty(sourceFilename)) 101 | return null; 102 | 103 | text = Uri.UnescapeDataString(text); 104 | return ProjectHelpers.ToAbsoluteFilePath(text, sourceFilename); 105 | } 106 | 107 | private static BitmapFrame LoadImage(string url) 108 | { 109 | try 110 | { 111 | if (url.StartsWith("data:", StringComparison.Ordinal)) 112 | { 113 | int index = url.IndexOf("base64,", StringComparison.Ordinal) + 7; 114 | byte[] imageBytes = Convert.FromBase64String(url.Substring(index)); 115 | 116 | using (MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length)) 117 | { 118 | // Must cache OnLoad before the stream is disposed 119 | return BitmapFrame.Create(ms, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); 120 | } 121 | } 122 | else if (url.EndsWith("svg")) 123 | { 124 | SvgDocument document = SvgDocument.Open(url); 125 | Bitmap bmp = document.Draw(); 126 | return BitmapFrame.Create(System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( 127 | bmp.GetHbitmap(), 128 | IntPtr.Zero, 129 | Int32Rect.Empty, 130 | BitmapSizeOptions.FromEmptyOptions())); 131 | } 132 | else if (url.Contains("://") || File.Exists(url)) 133 | { 134 | return BitmapFrame.Create(new Uri(url), BitmapCreateOptions.None, BitmapCacheOption.OnLoad); 135 | } 136 | } 137 | catch { } 138 | 139 | return null; 140 | } 141 | 142 | static T Freeze(T obj) where T : Freezable { obj.Freeze(); return obj; } 143 | 144 | static readonly BitmapFrame noPreview = Freeze(BitmapFrame.Create(new Uri("pack://application:,,,/CssTools;component/Resources/nopreview.png"))); 145 | public static void AddImageContent(IList qiContent, string url) 146 | { 147 | BitmapSource source; 148 | try 149 | { 150 | source = LoadImage(url); 151 | } 152 | catch (Exception ex) 153 | { 154 | qiContent.Add(new Image { Source = noPreview }); 155 | qiContent.Add(ex.Message); 156 | return; 157 | } 158 | 159 | if (source == null) 160 | { 161 | qiContent.Add(new Image { Source = noPreview }); 162 | qiContent.Add("Couldn't locate " + url); 163 | return; 164 | } 165 | 166 | // HWNDs are always 32-bit. 167 | // https://twitter.com/Schabse/status/406159104697049088 168 | // http://msdn.microsoft.com/en-us/library/aa384203.aspx 169 | var screen = Screen.FromHandle(new IntPtr(ProjectHelpers.DTE.ActiveWindow.HWnd)); 170 | Image image = new Image 171 | { 172 | Source = source, 173 | MaxWidth = screen.WorkingArea.Width / 2, 174 | MaxHeight = screen.WorkingArea.Height / 2, 175 | Stretch = Stretch.Uniform, 176 | StretchDirection = StretchDirection.DownOnly 177 | }; 178 | qiContent.Add(image); 179 | 180 | // Use a TextBuffer to show dynamic text with 181 | // the correct default styling. The presenter 182 | // uses the same technique to show strings in 183 | // QuickInfoItemView.CreateTextBuffer(). 184 | // Base64Tagger assumes that text from base64 185 | // images will never change. If that changes, 186 | // you must change that to handle changes. 187 | var size = WebEditor.ExportProvider.GetExport().Value.CreateTextBuffer(); 188 | size.SetText("Loading..."); 189 | 190 | source.OnDownloaded(() => size.SetText(source.PixelWidth + "×" + source.PixelHeight)); 191 | if (source.IsDownloading) 192 | { 193 | EventHandler failure = (s, e) => 194 | { 195 | image.Source = noPreview; 196 | size.SetText("Couldn't load image: " + e.ErrorException.Message); 197 | }; 198 | source.DecodeFailed += failure; 199 | source.DownloadFailed += failure; 200 | } 201 | 202 | qiContent.Add(size); 203 | } 204 | 205 | public void Dispose() 206 | { 207 | // Nothing to dispose 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/QuickInfo/Image/ImageQuickInfoController.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 CssTools 7 | { 8 | internal class ImageQuickInfoController : IIntellisenseController 9 | { 10 | private ITextView m_textView; 11 | private IList m_subjectBuffers; 12 | private ImageQuickInfoControllerProvider m_provider; 13 | 14 | internal ImageQuickInfoController(ITextView textView, IList subjectBuffers, ImageQuickInfoControllerProvider 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/Image/ImageQuickInfoControllerProvider.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 CssTools 9 | { 10 | [Export(typeof(IIntellisenseControllerProvider))] 11 | [Name("Image QuickInfo Controller")] 12 | [ContentType("CSS")] 13 | public class ImageQuickInfoControllerProvider : IIntellisenseControllerProvider 14 | { 15 | [Import] 16 | public IQuickInfoBroker QuickInfoBroker { get; set; } 17 | 18 | public IIntellisenseController TryCreateIntellisenseController(ITextView textView, IList subjectBuffers) 19 | { 20 | return new ImageQuickInfoController(textView, subjectBuffers, this); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/QuickInfo/Image/ImageQuickInfoSourceProvider.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Language.Intellisense; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Utilities; 5 | 6 | namespace CssTools 7 | { 8 | [Export(typeof(IQuickInfoSourceProvider))] 9 | [Name("Image QuickInfo Source")] 10 | [Order(Before = "Default Quick Info Presenter")] 11 | [ContentType("CSS")] 12 | internal class ImageQuickInfoSourceProvider : IQuickInfoSourceProvider 13 | { 14 | public IQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer) 15 | { 16 | return new ImageQuickInfo(textBuffer); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/QuickInfo/Selector/SelectorQuickInfo.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.Parser; 2 | using Microsoft.CSS.Core.TreeItems.Selectors; 3 | using Microsoft.CSS.Editor.Document; 4 | using Microsoft.VisualStudio.Language.Intellisense; 5 | using Microsoft.VisualStudio.Text; 6 | using Microsoft.Web.Languages.Less.Parser.Mixin; 7 | using Microsoft.Web.Languages.Scss.Parser.MixinDirective; 8 | using System; 9 | using System.Collections.Generic; 10 | 11 | namespace CssTools 12 | { 13 | internal class SelectorQuickInfo : IQuickInfoSource 14 | { 15 | private ITextBuffer _buffer; 16 | 17 | public SelectorQuickInfo(ITextBuffer subjectBuffer) 18 | { 19 | _buffer = subjectBuffer; 20 | } 21 | 22 | public void AugmentQuickInfoSession(IQuickInfoSession session, IList qiContent, out ITrackingSpan applicableToSpan) 23 | { 24 | applicableToSpan = null; 25 | 26 | if (session == null || qiContent == null) 27 | return; 28 | 29 | // Map the trigger point down to our buffer. 30 | SnapshotPoint? point = session.GetTriggerPoint(_buffer.CurrentSnapshot); 31 | 32 | if (!point.HasValue) 33 | return; 34 | 35 | var tree = CssEditorDocument.FromTextBuffer(_buffer); 36 | ParseItem item = tree.StyleSheet.ItemBeforePosition(point.Value.Position); 37 | 38 | if (item == null || !item.IsValid) 39 | return; 40 | 41 | Selector sel = item.FindType(); 42 | 43 | if (sel == null) 44 | return; 45 | 46 | // Mixins don't have specificity 47 | if (sel.SimpleSelectors.Count == 1) 48 | { 49 | var subSelectors = sel.SimpleSelectors[0].SubSelectors; 50 | 51 | if (subSelectors.Count == 1 && 52 | subSelectors[0] is LessMixinDeclaration && 53 | subSelectors[0] is ScssMixinDeclaration) 54 | return; 55 | } 56 | 57 | applicableToSpan = _buffer.CurrentSnapshot.CreateTrackingSpan(item.Start, item.Length, SpanTrackingMode.EdgeNegative); 58 | 59 | if (_buffer.ContentType.DisplayName.Equals("css", StringComparison.OrdinalIgnoreCase)) 60 | { 61 | qiContent.Add(GenerateContent(sel)); 62 | return; 63 | } 64 | } 65 | 66 | private static string GenerateContent(Selector sel) 67 | { 68 | SelectorSpecificity specificity = new SelectorSpecificity(sel); 69 | 70 | return "Selector specificity:\t\t" + specificity.ToString().Trim(); 71 | } 72 | 73 | public void Dispose() { } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/QuickInfo/Selector/SelectorQuickInfoController.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 CssTools 7 | { 8 | internal class SelectorQuickInfoController : IIntellisenseController 9 | { 10 | private ITextView m_textView; 11 | private IList m_subjectBuffers; 12 | private SelectorQuickInfoControllerProvider m_provider; 13 | 14 | internal SelectorQuickInfoController(ITextView textView, IList subjectBuffers, SelectorQuickInfoControllerProvider 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 27 | .MapDownToFirstMatch( 28 | new SnapshotPoint(m_textView.TextSnapshot, e.Position), 29 | PointTrackingMode.Positive, 30 | snapshot => m_subjectBuffers.Contains(snapshot.TextBuffer), 31 | PositionAffinity.Predecessor); 32 | 33 | if (point == null) 34 | return; 35 | 36 | ITrackingPoint triggerPoint = point.Value.Snapshot.CreateTrackingPoint(point.Value.Position, 37 | PointTrackingMode.Positive); 38 | 39 | if (m_provider.QuickInfoBroker.IsQuickInfoActive(m_textView)) 40 | return; 41 | 42 | m_provider.QuickInfoBroker.TriggerQuickInfo(m_textView, triggerPoint, true); 43 | } 44 | 45 | public void Detach(ITextView textView) 46 | { 47 | if (m_textView != textView) 48 | return; 49 | 50 | m_textView.MouseHover -= this.OnTextViewMouseHover; 51 | m_textView = null; 52 | } 53 | 54 | public void ConnectSubjectBuffer(ITextBuffer subjectBuffer) { } 55 | 56 | public void DisconnectSubjectBuffer(ITextBuffer subjectBuffer) { } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/QuickInfo/Selector/SelectorQuickInfoControllerProvider.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 CssTools 9 | { 10 | [Export(typeof(IIntellisenseControllerProvider))] 11 | [Name("Selector QuickInfo Controller")] 12 | [ContentType("CSS")] 13 | public class SelectorQuickInfoControllerProvider : IIntellisenseControllerProvider 14 | { 15 | [Import] 16 | public IQuickInfoBroker QuickInfoBroker { get; set; } 17 | 18 | public IIntellisenseController TryCreateIntellisenseController(ITextView textView, IList subjectBuffers) 19 | { 20 | return new SelectorQuickInfoController(textView, subjectBuffers, this); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/QuickInfo/Selector/SelectorQuickInfoSourceProvider.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.Composition; 2 | using Microsoft.VisualStudio.Language.Intellisense; 3 | using Microsoft.VisualStudio.Text; 4 | using Microsoft.VisualStudio.Utilities; 5 | 6 | namespace CssTools 7 | { 8 | [Export(typeof(IQuickInfoSourceProvider))] 9 | [Name("Selector QuickInfo Source")] 10 | [Order(Before = "Default Quick Info Presenter")] 11 | [ContentType("CSS")] 12 | internal class SelectorQuickInfoSourceProvider : IQuickInfoSourceProvider 13 | { 14 | public IQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer) 15 | { 16 | return new SelectorQuickInfo(textBuffer); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/QuickInfo/Selector/SelectorSpecificity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.CSS.Core; 4 | using Microsoft.CSS.Core.TreeItems.Selectors; 5 | using Microsoft.CSS.Core.TreeItems; 6 | 7 | namespace CssTools 8 | { 9 | internal class SelectorSpecificity 10 | { 11 | private Selector _selector; 12 | 13 | public SelectorSpecificity(Selector selector) 14 | { 15 | _selector = selector; 16 | Calculate(); 17 | } 18 | 19 | public int IDs { get; set; } 20 | public int Classes { get; set; } 21 | public int Elements { get; set; } 22 | public int PseudoClasses { get; set; } 23 | public int PseudoElements { get; set; } 24 | public int Attributes { get; set; } 25 | //public int Total { get; set; } 26 | 27 | public override string ToString() 28 | { 29 | return IDs + ", " + (Classes + PseudoClasses + Attributes) + ", " + (Elements + PseudoElements); 30 | } 31 | 32 | private void Calculate() 33 | { 34 | // IDs 35 | var visitorIDs = new CssItemCollector(); 36 | _selector.Accept(visitorIDs); 37 | 38 | if (visitorIDs.Items.Count > 0) 39 | IDs = visitorIDs.Items.Count;// *100; 40 | 41 | // Classes 42 | var visitorClasses = new CssItemCollector(); 43 | _selector.Accept(visitorClasses); 44 | 45 | if (visitorClasses.Items.Count > 0) 46 | Classes = visitorClasses.Items.Count;// *10; 47 | 48 | // Attributes 49 | var visitorAttribute = new CssItemCollector(); 50 | _selector.Accept(visitorAttribute); 51 | 52 | if (visitorAttribute.Items.Count > 0) 53 | Attributes = visitorAttribute.Items.Count;// *10; 54 | 55 | // Elements 56 | var visitorElements = new CssItemCollector(); 57 | _selector.Accept(visitorElements); 58 | Elements = visitorElements.Items.Where(i => i.Text != "*" && i.FindType() == null).Count(); 59 | 60 | // Pseudo Elements 61 | var visitorPseudoElementSelector = new CssItemCollector(); 62 | _selector.Accept(visitorPseudoElementSelector); 63 | 64 | var visitorPseudoElementFunctionSelector = new CssItemCollector(); 65 | _selector.Accept(visitorPseudoElementFunctionSelector); 66 | 67 | PseudoElements = visitorPseudoElementSelector.Items.Count + visitorPseudoElementFunctionSelector.Items.Count; 68 | 69 | // Pseudo Classes 70 | var visitorPseudoClassSelector = new CssItemCollector(); 71 | _selector.Accept(visitorPseudoClassSelector); 72 | 73 | var visitorPseudoClassFunctionSelector = new CssItemCollector(true); 74 | _selector.Accept(visitorPseudoClassFunctionSelector); 75 | 76 | int pseudoClases = visitorPseudoClassSelector.Items.Count(p => !p.IsPseudoElement()); 77 | pseudoClases += visitorPseudoClassFunctionSelector.Items.Where(p => !p.Text.StartsWith(":not(", StringComparison.Ordinal) && !p.Text.StartsWith(":matches(", StringComparison.Ordinal)).Count(); 78 | Elements += visitorPseudoClassSelector.Items.Count(p => p.IsPseudoElement()); 79 | 80 | if (pseudoClases > 0) 81 | PseudoClasses = pseudoClases;// *10; 82 | 83 | // Total 84 | //Total = IDs + Classes + Attributes + Elements + PseudoElements + PseudoClasses; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/QuickInfo/ValueOrder/RemoveCssSignatureHelpSource.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.Parser; 2 | using Microsoft.CSS.Core.TreeItems; 3 | using Microsoft.CSS.Editor.Document; 4 | using Microsoft.VisualStudio.Language.Intellisense; 5 | using Microsoft.VisualStudio.Text; 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | namespace CssTools 10 | { 11 | internal class RemoveCssSignatureHelpSource : ISignatureHelpSource 12 | { 13 | private ITextBuffer _buffer; 14 | 15 | public RemoveCssSignatureHelpSource(ITextBuffer buffer) 16 | { 17 | _buffer = buffer; 18 | } 19 | 20 | public void AugmentSignatureHelpSession(ISignatureHelpSession session, IList signatures) 21 | { 22 | SnapshotPoint? point = session.GetTriggerPoint(_buffer.CurrentSnapshot); 23 | if (!point.HasValue) 24 | return; 25 | 26 | CssEditorDocument document = CssEditorDocument.FromTextBuffer(_buffer); 27 | ParseItem item = document.StyleSheet.ItemBeforePosition(point.Value.Position); 28 | 29 | if (item == null) 30 | return; 31 | 32 | Declaration dec = item.FindType(); 33 | if (dec == null || dec.PropertyName == null || dec.Colon == null) 34 | return; 35 | 36 | foreach (ISignature signature in signatures) 37 | { 38 | if (signature is ValueOrderSignature) 39 | { 40 | signatures.RemoveAt(signatures.Count - 1); 41 | break; 42 | } 43 | } 44 | } 45 | 46 | public ISignature GetBestMatch(ISignatureHelpSession session) 47 | { 48 | return (session.Signatures != null && session.Signatures.Count > 0) 49 | ? session.Signatures[0] 50 | : null; 51 | } 52 | 53 | private bool m_isDisposed; 54 | public void Dispose() 55 | { 56 | if (!m_isDisposed) 57 | { 58 | GC.SuppressFinalize(this); 59 | m_isDisposed = true; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/QuickInfo/ValueOrder/ValueOrderSignature.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Language.Intellisense; 2 | using Microsoft.VisualStudio.Text; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Collections.ObjectModel; 6 | using System.Globalization; 7 | 8 | namespace CssTools 9 | { 10 | public class ValueOrderSignature : ISignature 11 | { 12 | private string _propertyName; 13 | private string _syntax; 14 | private string _description; 15 | private string _content; 16 | private CssPropertyNameParameter _nameParam; 17 | private IParameter _currentParam; 18 | private ITrackingSpan _trackingSpan; 19 | private ISignatureHelpSession _session; 20 | 21 | public event EventHandler CurrentParameterChanged; 22 | 23 | public ValueOrderSignature( 24 | string syntax, 25 | string description, 26 | ITrackingSpan trackingSpan, 27 | ISignatureHelpSession session) 28 | { 29 | 30 | _propertyName = "Syntax"; 31 | _syntax = syntax ?? string.Empty; 32 | _description = description; 33 | _trackingSpan = trackingSpan; 34 | 35 | _content = string.Format(CultureInfo.InvariantCulture, "{0}: {1}", _propertyName, _syntax); 36 | _nameParam = new CssPropertyNameParameter(this); 37 | _currentParam = _nameParam; 38 | 39 | _session = session; 40 | 41 | // In order to dismiss this tip at the appropriate time, I need to listen 42 | // to changes in the text buffer 43 | if (_trackingSpan != null && _session != null) 44 | { 45 | _session.Dismissed += OnSessionDismissed; 46 | _trackingSpan.TextBuffer.Changed += OnTextBufferChanged; 47 | } 48 | } 49 | 50 | public ITrackingSpan ApplicableToSpan 51 | { 52 | get { return _trackingSpan; } 53 | } 54 | 55 | public string PropertyName 56 | { 57 | get { return _propertyName; } 58 | } 59 | 60 | public string Content 61 | { 62 | get { return _content; } 63 | } 64 | 65 | public IParameter CurrentParameter 66 | { 67 | get { return _nameParam; } 68 | 69 | set 70 | { 71 | if (value != _currentParam) 72 | { 73 | IParameter oldParam = _currentParam; 74 | _currentParam = value; 75 | 76 | if (CurrentParameterChanged != null) 77 | { 78 | CurrentParameterChanged(this, new CurrentParameterChangedEventArgs(oldParam, _currentParam)); 79 | } 80 | } 81 | } 82 | } 83 | 84 | public string Documentation 85 | { 86 | get { return _description; } 87 | } 88 | 89 | public ReadOnlyCollection Parameters 90 | { 91 | get 92 | { 93 | IList parameters = new List(); 94 | parameters.Add(_nameParam); 95 | 96 | return new ReadOnlyCollection(parameters); 97 | } 98 | } 99 | 100 | /// 101 | /// This is called when there isn't enough room on the screen to show the normal content 102 | /// 103 | public string PrettyPrintedContent 104 | { 105 | get { return Content; } 106 | } 107 | 108 | /// 109 | /// I'm about to be destroyed, so stop listening to events 110 | /// 111 | private void OnSessionDismissed(object sender, System.EventArgs eventArgs) 112 | { 113 | if (_trackingSpan != null) 114 | { 115 | _trackingSpan.TextBuffer.Changed -= OnTextBufferChanged; 116 | } 117 | 118 | if (_session != null) 119 | { 120 | _session.Dismissed -= OnSessionDismissed; 121 | _session = null; 122 | } 123 | } 124 | 125 | /// 126 | /// Check if the property name in the text buffer has changed. 127 | /// If so, then dismiss the syntax help tip. 128 | /// 129 | private void OnTextBufferChanged(object sender, TextContentChangedEventArgs eventArgs) 130 | { 131 | if (_trackingSpan != null && _session != null) 132 | { 133 | ITextSnapshot snapshot = _trackingSpan.TextBuffer.CurrentSnapshot; 134 | SnapshotPoint startPoint = _trackingSpan.GetStartPoint(snapshot); 135 | bool propertyNameStillValid = false; 136 | 137 | if (startPoint.Position + _propertyName.Length <= snapshot.Length) 138 | { 139 | // Get the current text at the beginning of the tracking span. 140 | 141 | string text = snapshot.GetText(startPoint.Position, _propertyName.Length); 142 | 143 | char afterText = (startPoint.Position + _propertyName.Length < snapshot.Length) 144 | ? snapshot.GetText(startPoint.Position + _propertyName.Length, 1)[0] 145 | : '\0'; 146 | 147 | if (string.Equals(text, _propertyName, StringComparison.OrdinalIgnoreCase) && 148 | !char.IsLetterOrDigit(afterText) && 149 | afterText != '-') 150 | { 151 | // The correct property name is still in the code 152 | propertyNameStillValid = true; 153 | } 154 | } 155 | 156 | if (!propertyNameStillValid) 157 | { 158 | _session.Dismiss(); 159 | } 160 | } 161 | } 162 | } 163 | 164 | internal class CssPropertyNameParameter : IParameter 165 | { 166 | private ValueOrderSignature _signature; 167 | 168 | public CssPropertyNameParameter(ValueOrderSignature signature) 169 | { 170 | _signature = signature; 171 | } 172 | 173 | public string Documentation 174 | { 175 | get { return null; } 176 | } 177 | 178 | public Span Locus 179 | { 180 | get { return new Span(0, _signature.PropertyName.Length); } 181 | } 182 | 183 | public string Name 184 | { 185 | get { return _signature.PropertyName; } 186 | } 187 | 188 | public Span PrettyPrintedLocus 189 | { 190 | get { return Locus; } 191 | } 192 | 193 | public ISignature Signature 194 | { 195 | get { return _signature; } 196 | } 197 | } 198 | 199 | } 200 | -------------------------------------------------------------------------------- /src/QuickInfo/ValueOrder/ValueOrderSignatureHelpSource.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSS.Core.Parser; 2 | using Microsoft.CSS.Core.TreeItems; 3 | using Microsoft.CSS.Editor.Document; 4 | using Microsoft.VisualStudio.Language.Intellisense; 5 | using Microsoft.VisualStudio.Text; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Windows.Threading; 9 | 10 | namespace CssTools 11 | { 12 | internal class ValueOrderSignatureHelpSource : ISignatureHelpSource 13 | { 14 | private ITextBuffer _buffer; 15 | 16 | public ValueOrderSignatureHelpSource(ITextBuffer buffer) 17 | { 18 | _buffer = buffer; 19 | } 20 | 21 | public void AugmentSignatureHelpSession(ISignatureHelpSession session, IList signatures) 22 | { 23 | SnapshotPoint? point = session.GetTriggerPoint(_buffer.CurrentSnapshot); 24 | if (!point.HasValue) 25 | return; 26 | 27 | CssEditorDocument document = CssEditorDocument.FromTextBuffer(_buffer); 28 | ParseItem item = document.StyleSheet.ItemBeforePosition(point.Value.Position); 29 | 30 | if (item == null) 31 | return; 32 | 33 | Declaration dec = item.FindType(); 34 | if (dec == null || dec.PropertyName == null || dec.Colon == null) 35 | return; 36 | 37 | int length = dec.Length - (dec.Colon.Start - dec.Start); 38 | var span = _buffer.CurrentSnapshot.CreateTrackingSpan(dec.Colon.Start, length, SpanTrackingMode.EdgeNegative); 39 | 40 | ValueOrderFactory.AddSignatures method = ValueOrderFactory.GetMethod(dec); 41 | 42 | if (method != null) 43 | { 44 | signatures.Clear(); 45 | method(session, signatures, dec, span); 46 | 47 | Dispatcher.CurrentDispatcher.BeginInvoke( 48 | new Action(() => 49 | { 50 | if (session == null || session.Properties == null) 51 | return; 52 | 53 | session.Properties.AddProperty("dec", dec); 54 | session.Match(); 55 | }), 56 | DispatcherPriority.Normal, null); 57 | } 58 | } 59 | 60 | public ISignature GetBestMatch(ISignatureHelpSession session) 61 | { 62 | int number = 0; 63 | 64 | if (session.Properties.ContainsProperty("dec")) 65 | { 66 | Declaration dec = session.Properties["dec"] as Declaration; 67 | string methodName = ValueOrderFactory.GetMethod(dec).Method.Name; 68 | if (dec.Values.Count > 0 && (methodName == "Margins" || methodName == "Corners")) 69 | { 70 | number = 4 - dec.Values.Count; 71 | } 72 | } 73 | 74 | return (session.Signatures != null && session.Signatures.Count > number && number > -1) 75 | ? session.Signatures[number] 76 | : null; 77 | } 78 | 79 | private bool m_isDisposed; 80 | public void Dispose() 81 | { 82 | if (!m_isDisposed) 83 | { 84 | GC.SuppressFinalize(this); 85 | m_isDisposed = true; 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/QuickInfo/ValueOrder/ValueOrderSignatureHelpSourceProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Language.Intellisense; 2 | using Microsoft.VisualStudio.Text; 3 | using Microsoft.VisualStudio.Utilities; 4 | using Microsoft.Web.Core.ContentTypes; 5 | using System.ComponentModel.Composition; 6 | 7 | namespace CssTools 8 | { 9 | [Export(typeof(ISignatureHelpSourceProvider))] 10 | [Name("Value Order Signature Help Source")] 11 | [Order(Before = "CSS Signature Help Source")] 12 | [ContentType(CssContentTypeDefinition.CssContentType)] 13 | internal class ValueOrderSignatureHelpSourceProvider : ISignatureHelpSourceProvider 14 | { 15 | public ISignatureHelpSource TryCreateSignatureHelpSource(ITextBuffer textBuffer) 16 | { 17 | return new ValueOrderSignatureHelpSource(textBuffer); 18 | } 19 | } 20 | 21 | [Export(typeof(ISignatureHelpSourceProvider))] 22 | [Name("Value Order Signature Help Source2")] 23 | [Order(After = "Default")] 24 | [ContentType(CssContentTypeDefinition.CssContentType)] 25 | internal class RemoveCssSignatureHelpSourceProvider : ISignatureHelpSourceProvider 26 | { 27 | public ISignatureHelpSource TryCreateSignatureHelpSource(ITextBuffer textBuffer) 28 | { 29 | return new RemoveCssSignatureHelpSource(textBuffer); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Resources/Browsers/c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/src/Resources/Browsers/c.png -------------------------------------------------------------------------------- /src/Resources/Browsers/e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/src/Resources/Browsers/e.png -------------------------------------------------------------------------------- /src/Resources/Browsers/ff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/src/Resources/Browsers/ff.png -------------------------------------------------------------------------------- /src/Resources/Browsers/ie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/src/Resources/Browsers/ie.png -------------------------------------------------------------------------------- /src/Resources/Browsers/o.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/src/Resources/Browsers/o.png -------------------------------------------------------------------------------- /src/Resources/Browsers/s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/src/Resources/Browsers/s.png -------------------------------------------------------------------------------- /src/Resources/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/src/Resources/Icon.png -------------------------------------------------------------------------------- /src/Resources/nopreview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/src/Resources/nopreview.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/source.extension.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This file was generated by Extensibility Tools v1.10.196 4 | // 5 | // ------------------------------------------------------------------------------ 6 | namespace CssTools 7 | { 8 | static class Vsix 9 | { 10 | public const string Id = "0020efc9-e999-4e6f-a2b6-604127f480bc"; 11 | public const string Name = "CSS Tools"; 12 | public const string Description = @"Provides additional features to the CSS editor in Visual Studio."; 13 | public const string Language = "en-US"; 14 | public const string Version = "1.1"; 15 | public const string Author = "Mads Kristensen"; 16 | public const string Tags = "css, less, sass, scss, stylesheet"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/source.extension.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/madskristensen/CssTools/c4efd1b759aeac84dae23d92345fd3534a3b18b5/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 | CSS Tools 122 | 123 | 124 | Provides additional features to the CSS editor in Visual Studio. 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 | CSS Tools 6 | Provides additional features to the CSS editor in Visual Studio. 7 | https://github.com/madskristensen/CssTools 8 | Resources\LICENSE 9 | https://github.com/madskristensen/CssTools/blob/master/CHANGELOG.md 10 | Resources\Icon.png 11 | Resources\Icon.png 12 | css, less, sass, scss, stylesheet 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | --------------------------------------------------------------------------------