diff --git a/ImageCommentsExtension/CommentImage.cs b/ImageCommentsExtension/CommentImage.cs new file mode 100644 index 0000000..f1e3153 --- /dev/null +++ b/ImageCommentsExtension/CommentImage.cs @@ -0,0 +1,209 @@ +using System.IO; +using System.Windows; + +namespace LM.ImageComments.EditorComponent +{ + using System; + using System.Collections.Generic; + using System.Windows.Controls; + using System.Windows.Media; + using System.Windows.Media.Imaging; + + public class ImageAttributes + { + public string Url; + public double Scale; + public double Opacity; + public Color Background; + private const double Tolerance = 0.001; + + public ImageAttributes() + { + Url = ""; + Scale = 1.0; + Opacity = 1.0; + } + + public override bool Equals(object obj) + { + if (obj == null) + return false; + if (!(obj is ImageAttributes other)) + return false; + return other.Url == Url && + Math.Abs(other.Scale - Scale) < Tolerance && + Math.Abs(other.Opacity - Opacity) < Tolerance && + other.Background.Equals(Background); + } + } + + /// + /// Sub-class of Image with convenient URL-based Source changing + /// + public class CommentImage : Image + { + + private readonly VariableExpander _variableExpander; + private FileSystemWatcher _watcher; + + public ImageAttributes Attributes; + + public CommentImage(VariableExpander variableExpander) + : base() + { + _variableExpander = variableExpander ?? throw new ArgumentNullException(nameof(variableExpander)); + this.VisualBitmapScalingMode = BitmapScalingMode.HighQuality; + } + + private bool LoadFromUri(string rawUri, string sourceFileDir, Action refreshAction, out string errorString) + { + if (string.IsNullOrWhiteSpace(rawUri)) + { + Source = null; + errorString = "No image specified"; + return false; + } + + var expandedUrl = _variableExpander.ProcessText(rawUri); + if (!File.Exists(expandedUrl)) //TODO: Refactor this eg. post processing step + { + // if the file does not exists, but we have an existing "docfx.json", lets try to find file in "$(ProjectDir)\images" directory + var jsonFile = _variableExpander.ProcessText("$(ProjectDir)\\docfx.json"); + if (File.Exists(jsonFile)) + { + // Example: we replace in "..\\images\picture.png" all the ".." with "$ProjectDir" --> "$ProjectDir\\images\\picture.png" + expandedUrl = rawUri.Replace("..", "$(ProjectDir)"); + expandedUrl = _variableExpander.ProcessText(expandedUrl); + } + } + + var success = Uri.TryCreate(_variableExpander.ProcessText(expandedUrl), UriKind.Absolute, out var uri); + var canLoadData = success && DataUriLoader.CanLoad(uri); + var canLoadFromWeb = success && WebLoader.CanLoad(uri); + if (canLoadData) + { + //TODO [!]: Currently, this loading system prevents images from being changed on disk, fix this + // e.g. using http://stackoverflow.com/questions/1763608/display-an-image-in-wpf-without-holding-the-file-open + Source = BitmapFrame.Create(DataUriLoader.Load(uri)); + } + else if (canLoadFromWeb) + { + expandedUrl = WebLoader.Load(uri); + } + else if (!success && !Path.IsPathRooted(expandedUrl) && sourceFileDir != null) + { + expandedUrl = Path.Combine(sourceFileDir, expandedUrl); + expandedUrl = Path.GetFullPath((new Uri(expandedUrl)).LocalPath); + } + + if (!canLoadData && File.Exists(expandedUrl)) + { + var data = new MemoryStream(File.ReadAllBytes(expandedUrl)); + Source = BitmapFrame.Create(data); + // Create file system watcher to update changed image file. + _watcher = new FileSystemWatcher + { + //NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.Size, + Path = Path.GetDirectoryName(expandedUrl), + Filter = Path.GetFileName(expandedUrl) + }; + var w = _watcher; + + void Refresh(object sender, FileSystemEventArgs e) + { + try + { + var enableRaisingEvents = w.EnableRaisingEvents; + w.EnableRaisingEvents = false; + if (!enableRaisingEvents) + return; + + Attributes.Url = null; + refreshAction(); + } + catch + { + // ignored + } + } + + _watcher.Changed += Refresh; + _watcher.Renamed += Refresh; + _watcher.Deleted += Refresh; + _watcher.EnableRaisingEvents = true; + + errorString = null; + return true; + } + + Source = null; + errorString = $"Could not load image '{uri}' (resolved to '{expandedUrl}')"; + return false; + } + + /// + /// Sets image source and size (by scale factor) + /// + /// The directory where the text document referencing the image lives + /// The image attributes used to set the image + /// Error message if image couldn't be loaded, otherwise null + /// The action to be performed if the image was successfully refreshed + /// Returns true if image was successfully loaded, otherwise false + public bool TrySet(string directory, ImageAttributes attribs, out string error, Action refreshAction) + { + // Remove old watcher. + var watcher = _watcher; + _watcher = null; + watcher?.Dispose(); + // --- + error = null; + + try + { + if (LoadFromUri(attribs.Url, directory, refreshAction, out var errorString)) + { + Attributes = attribs; + ProcessBitmap(); + } + + error = errorString; + } + catch (Exception ex) + { + Source = null; + error = ex.Message; + return false; + } + + return true; + } + + private void ProcessBitmap() + { + if (Source == null) + return; + + var scale = Math.Min(Math.Max(Attributes.Scale, 0.01), 100); + + var scaledRect = new Rect(0, 0, (int)(Source.Width * scale), (int)(Source.Height * scale)); + var rect = new Rect(0, 0, (int)Source.Width, (int)Source.Height); + var visual = new DrawingVisual(); + var context = visual.RenderOpen(); + context.PushTransform(new ScaleTransform(scale, scale)); + context.PushOpacity(Math.Min(Math.Max(Attributes.Opacity, 0),1)); + context.DrawRectangle(new SolidColorBrush(Attributes.Background), null, rect); + context.DrawImage(Source, rect); + context.Close(); + + var render = new RenderTargetBitmap((int)scaledRect.Width, (int)scaledRect.Height, + 96, 96, PixelFormats.Pbgra32); + RenderOptions.SetEdgeMode(render, EdgeMode.Aliased); + RenderOptions.SetBitmapScalingMode(render, BitmapScalingMode.HighQuality); + + render.Render(visual); + Source = BitmapFrame.Create(render); + Height = Source.Height; + Width = Source.Width; + } + } +} diff --git a/ImageCommentsExtension/ErrorTaggerProvider.cs b/ImageCommentsExtension/ErrorTaggerProvider.cs index 38608bf..7025d7d 100644 --- a/ImageCommentsExtension/ErrorTaggerProvider.cs +++ b/ImageCommentsExtension/ErrorTaggerProvider.cs @@ -12,6 +12,7 @@ ContentType("CSharp"), ContentType("C/C++"), ContentType("Basic"), + ContentType("code++.F#"), ContentType("F#"), ContentType("JScript"), ContentType("Python") diff --git a/ImageCommentsExtension/ExceptionHandler.cs b/ImageCommentsExtension/ExceptionHandler.cs index 059cb87..76a2f11 100644 --- a/ImageCommentsExtension/ExceptionHandler.cs +++ b/ImageCommentsExtension/ExceptionHandler.cs @@ -9,7 +9,7 @@ public static void Notify(Exception ex, bool showMessage) { ThreadHelper.ThrowIfNotOnUIThread(); - string message = string.Format("{0}: {1}", DateTime.Now, ex.ToString()); + string message = $"{DateTime.Now}: {ex.ToString()}"; Console.WriteLine(message); if (showMessage) { diff --git a/ImageCommentsExtension/ImageAdornmentManager.cs b/ImageCommentsExtension/ImageAdornmentManager.cs index bcdf25d..82e028e 100644 --- a/ImageCommentsExtension/ImageAdornmentManager.cs +++ b/ImageCommentsExtension/ImageAdornmentManager.cs @@ -27,7 +27,7 @@ public class ImageAdornmentManager : ITagger, IDisposable public static bool Enabled { get; set; } // Dictionary to map line number to image - internal Dictionary Images { get; set; } + internal Dictionary Images { get; set; } /// /// Initializes static members of the class @@ -52,9 +52,8 @@ public ImageAdornmentManager(IWpfTextView view) { _view = view; _layer = view.GetAdornmentLayer("ImageCommentLayer"); - Images = new Dictionary(); + Images = new Dictionary(); _view.LayoutChanged += LayoutChangedHandler; - _contentTypeName = view.TextBuffer.ContentType.TypeName; _view.TextBuffer.ContentTypeChanged += contentTypeChangedHandler; @@ -79,7 +78,6 @@ private void LayoutChangedHandler(object sender, TextViewLayoutChangedEventArgs _errorTags.Clear(); TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(new SnapshotSpan(_view.TextSnapshot, new Span(0, _view.TextSnapshot.Length)))); - OnTagsChanged(new SnapshotSpan(_view.TextSnapshot, new Span(0, _view.TextSnapshot.Length))); foreach (var line in _view.TextViewLines) // TODO [?]: implement more sensible handling of removing error tags, then use e.NewOrReformattedLines @@ -122,20 +120,19 @@ private void CreateVisuals(ITextViewLine line, int lineNumber, string absFilenam ThreadHelper.ThrowIfNotOnUIThread(); var directory = absFilename!=null ? System.IO.Path.GetDirectoryName(absFilename) : null; - var lineText = line.Extent.GetText().Split(new string[] { "\r\n", "\r" }, StringSplitOptions.None)[0]; + var lineText = line.Extent.GetText().Split(new[] { "\r\n", "\r" }, StringSplitOptions.None)[0]; var matchIndex = ImageCommentParser.Match(_contentTypeName, lineText, out var matchedText); if (matchIndex >= 0) { - lineText = line.Extent.GetText().Split(new string[] { "\r\n", "\r" }, StringSplitOptions.None)[0]; + //lineText = line.Extent.GetText().Split(new string[] { "\r\n", "\r" }, StringSplitOptions.None)[0]; // Get coordinates of text var start = line.Extent.Start.Position + matchIndex; var end = line.Start + (line.Extent.Length - 1); var span = new SnapshotSpan(_view.TextSnapshot, Span.FromBounds(start, end)); - var bgColor = new Color(); - ImageCommentParser.TryParse(matchedText, out var imageUrl, out var scale, ref bgColor, out var xmlParseError); + ImageCommentParser.TryParse(matchedText, out var parsedImgData, out var parsingError); - if (xmlParseError != null) + if (parsingError != null) { if (Images.ContainsKey(lineNumber)) { @@ -144,31 +141,26 @@ private void CreateVisuals(ITextViewLine line, int lineNumber, string absFilenam } _errorTags.Add(new TagSpan(span, - new ErrorTag("XML parse error", $"Problem with comment format: {xmlParseError}"))); + new ErrorTag("XML parse error", $"Problem with comment format: {parsingError}"))); return; } - MyImage image; string loadingMessage = null; // Check for and update existing image - MyImage existingImage = Images.ContainsKey(lineNumber) ? Images[lineNumber] : null; - if (existingImage != null) + CommentImage image = Images.ContainsKey(lineNumber) ? Images[lineNumber] : null; + if (image != null) { - image = existingImage; - if (existingImage.Url != imageUrl || existingImage.BgColor!= bgColor) // URL different, so set new source - { - existingImage.TrySet(directory, imageUrl, scale, bgColor, out loadingMessage, () => CreateVisuals(line, lineNumber, absFilename)); - } else if (existingImage.Url == imageUrl && Math.Abs(existingImage.Scale - scale) > 0.0001) // URL same but scale changed + if (!image.Attributes.Equals(parsedImgData)) // URL different, so set new source { - existingImage.Scale = scale; + image.TrySet(directory, parsedImgData, out loadingMessage, () => CreateVisuals(line, lineNumber, absFilename)); } } else // No existing image, so create new one { - image = new MyImage(_variableExpander); - image.TrySet(directory, imageUrl, scale, bgColor, out loadingMessage, () => CreateVisuals(line, lineNumber, absFilename)); + image = new CommentImage(_variableExpander); + image.TrySet(directory, parsedImgData, out loadingMessage, () => CreateVisuals(line, lineNumber, absFilename)); Images.Add(lineNumber, image); } diff --git a/ImageCommentsExtension/ImageAdornmentManagerFactory.cs b/ImageCommentsExtension/ImageAdornmentManagerFactory.cs index 00de4b0..7efa565 100644 --- a/ImageCommentsExtension/ImageAdornmentManagerFactory.cs +++ b/ImageCommentsExtension/ImageAdornmentManagerFactory.cs @@ -15,6 +15,7 @@ namespace LM.ImageComments.EditorComponent ContentType("CSharp"), ContentType("C/C++"), ContentType("Basic"), + ContentType("code++.F#"), ContentType("F#"), ContentType("JScript"), ContentType("Python") diff --git a/ImageCommentsExtension/ImageCommentParser.cs b/ImageCommentsExtension/ImageCommentParser.cs index a8be4c7..1e26a15 100644 --- a/ImageCommentsExtension/ImageCommentParser.cs +++ b/ImageCommentsExtension/ImageCommentParser.cs @@ -5,8 +5,6 @@ namespace LM.ImageComments.EditorComponent using System; using System.Globalization; using System.Text.RegularExpressions; - using System.Windows.Media; - using System.Xml; using System.Xml.Linq; // TODO [?]: Could make this a non-static class and use instances, but ensure a new instance is created when content type of a view is changed. @@ -55,11 +53,12 @@ public static int Match(string contentTypeName, string lineText, out string matc { Match commentMatch; Match indentMatch; - switch (contentTypeName) + switch(contentTypeName) { case "C/C++": case "CSharp": case "JScript": + case "code++.F#": case "F#": commentMatch = CsharpImageCommentRegex.Match(lineText); indentMatch = CsharpIndentRegex.Match(lineText); @@ -90,17 +89,13 @@ public static int Match(string contentTypeName, string lineText, out string matc /// Looks for well formed image comment in line of text and tries to parse parameters /// /// Input: Line of text in editor window - /// Output: URL of image - /// Output: Scale factor of image - /// The backgroundcolor if set - /// An error string describing the problem if parsing failed + /// Output: The collected image data + /// An error string describing the problem if parsing failed /// Returns true if successful, otherwise false - public static bool TryParse(string matchedText, out string imageUrl, out double imageScale, ref Color bgColor, out string error) + public static bool TryParse(string matchedText, out ImageAttributes imageData, out string parsingError) { - error = null; - imageUrl = ""; - imageScale = 0; // See MyImage.cs for explanation of default value here - + imageData = new ImageAttributes(); + // Try parse text if (matchedText != "") { @@ -117,45 +112,74 @@ public static bool TryParse(string matchedText, out string imageUrl, out double } if (srcAttr == null) { - error = "src (or url) attribute not specified."; + parsingError = "src (or url) attribute not specified."; return false; } - imageUrl = srcAttr.Value; + imageData.Url = srcAttr.Value; + + //scale XAttribute scaleAttr = imgEl.Attribute("scale"); if (scaleAttr != null) { - double.TryParse(scaleAttr.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out imageScale); + double.TryParse(scaleAttr.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out imageData.Scale); } + //opacity + XAttribute opacityAttr = imgEl.Attribute("opacity"); + if (opacityAttr != null) + { + double.TryParse(opacityAttr.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out imageData.Opacity); + } + + //background color XAttribute bgColorAttr = imgEl.Attribute("bgcolor"); if (bgColorAttr != null) { - UInt32 color; - if( UInt32.TryParse(bgColorAttr.Value.Replace("#", "").Replace("0x", ""), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out color) ) + var colorString = bgColorAttr.Value.Replace("#", "").Replace("0x", ""); + + //expand short hand color format + if (colorString.Length == 3) { - bgColor.A = 255; - bgColor.B = (byte)color; - bgColor.G = (byte)(color>>8); - bgColor.R = (byte)(color>>16); + colorString = String.Format("{0}{0}{1}{1}{2}{2}", + colorString[0], colorString[1], colorString[2]); + } + + if ((colorString.Length == 6 || colorString.Length == 8) && + UInt32.TryParse(colorString, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var color) ) + { + if (colorString.Length == 6) + { + imageData.Background.A = 255; + imageData.Background.B = (byte)color; + imageData.Background.G = (byte)(color >> 8); + imageData.Background.R = (byte)(color >> 16); + } + else + { + imageData.Background.A = (byte)color; + imageData.Background.B = (byte)(color >> 8); + imageData.Background.G = (byte)(color >> 16); + imageData.Background.R = (byte)(color >> 24); + } + } } else { - bgColor.A = 0; + imageData.Background.A = 0; } + + parsingError = null; return true; } catch (Exception ex) { - error = ex.Message; + parsingError = ex.Message; return false; } } - else - { - error = " or tag not in correct format."; - return false; - } + parsingError = @" or tag not in correct format."; + return false; } } } diff --git a/ImageCommentsExtension/ImageCommentsEditorComponent.csproj b/ImageCommentsExtension/ImageCommentsEditorComponent.csproj index 2509cd5..02db67d 100644 --- a/ImageCommentsExtension/ImageCommentsEditorComponent.csproj +++ b/ImageCommentsExtension/ImageCommentsEditorComponent.csproj @@ -94,7 +94,7 @@ - + @@ -460,15 +460,7 @@ - - - - ..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll - True - True - - - + diff --git a/ImageCommentsExtension/MyImage.cs b/ImageCommentsExtension/MyImage.cs deleted file mode 100644 index 5ef44d8..0000000 --- a/ImageCommentsExtension/MyImage.cs +++ /dev/null @@ -1,196 +0,0 @@ -using System.IO; -using System.Windows; -using LM.ImageComments; -namespace LM.ImageComments.EditorComponent -{ - using System; - using System.Windows.Controls; - using System.Windows.Media; - using System.Windows.Media.Imaging; - - /// - /// Sub-class of Image with convenient URL-based Source changing - /// - internal class MyImage : Image - { - private double _scale; - private VariableExpander _variableExpander; - private FileSystemWatcher _watcher; - - public string Url { get; private set; } - public Color BgColor { get; private set; } - - public MyImage(VariableExpander variableExpander) - : base() - { - if (variableExpander == null) - { - throw new ArgumentNullException("variableExpander"); - } - _variableExpander = variableExpander; - } - - /// - /// Scale image if value is greater than 0, otherwise use source dimensions - /// - public double Scale - { - get { return _scale; } - set - { - _scale = value; - if (this.Source != null) - { - if (value > 0) - { - this.Width = this.Source.Width * value; - this.Height = this.Source.Height * value; - } - else - { - this.Width = this.Source.Width; - this.Height = this.Source.Height; - } - } - } - } - - /// - /// Sets image source and size (by scale factor) - /// - /// The directory where the text document referencing the image lives - /// The url to the image - /// If > 0, scales the image by the specified amount, otherwise uses source image dimensions - /// The background color used for transparent images - /// Error message if image couldn't be loaded, otherwise null - /// The action to be performed if the image was successfully refreshed - /// Returns true if image was successfully loaded, otherwise false - public bool TrySet(string directory, string imageUrl, double scale, Color bgColor, out String error, Action refreshAction) - { - // Remove old watcher. - var watcher = _watcher; - _watcher = null; - watcher?.Dispose(); - // --- - error = null; - - if (string.IsNullOrWhiteSpace(imageUrl)) - { - Source = null; - return false; - } - - try - { - var expandedUrl = _variableExpander.ProcessText(imageUrl); - if (!File.Exists(expandedUrl)) //TODO: Refactor this eg. post processing step - { - // if the file does not exists, but we have an existing "docfx.json", lets try to find file in "$(ProjectDir)\images" directory - var jsonFile = _variableExpander.ProcessText("$(ProjectDir)\\docfx.json"); - if (File.Exists(jsonFile)) - { - // Example: we replace in "..\\images\picture.png" all the ".." with "$ProjectDir" --> "$ProjectDir\\images\\picture.png" - imageUrl = imageUrl.Replace("..", "$(ProjectDir)"); - expandedUrl = _variableExpander.ProcessText(imageUrl); - } - } - - var success = Uri.TryCreate(_variableExpander.ProcessText(expandedUrl), UriKind.Absolute, out var uri); - var canLoadData = success && DataUriLoader.CanLoad(uri); - var canLoadFromWeb = success && WebLoader.CanLoad(uri); - if (canLoadData) - { - //TODO [!]: Currently, this loading system prevents images from being changed on disk, fix this - // e.g. using http://stackoverflow.com/questions/1763608/display-an-image-in-wpf-without-holding-the-file-open - Source = BitmapFrame.Create(DataUriLoader.Load(uri)); - } - else if(canLoadFromWeb) - { - expandedUrl = WebLoader.Load(uri); - } - else if (!success && !Path.IsPathRooted(expandedUrl) && directory != null) - { - expandedUrl = Path.Combine(directory, expandedUrl); - expandedUrl = Path.GetFullPath((new Uri(expandedUrl)).LocalPath); - } - - if (!canLoadData && File.Exists(expandedUrl)) - { - var data = new MemoryStream(File.ReadAllBytes(expandedUrl)); - Source = BitmapFrame.Create(data); - // Create file system watcher to update changed image file. - _watcher = new FileSystemWatcher - { - //NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.Size, - Path = Path.GetDirectoryName(expandedUrl), - Filter = Path.GetFileName(expandedUrl) - }; - var w = _watcher; - FileSystemEventHandler refresh = delegate - { - try - { - var enableRaisingEvents = w.EnableRaisingEvents; - w.EnableRaisingEvents = false; - if (enableRaisingEvents) - { - Url = null; - refreshAction(); - } - } - catch { } - }; - _watcher.Changed += refresh; - _watcher.Renamed += (s, a) => refresh(s, a); - _watcher.Deleted += refresh; - _watcher.EnableRaisingEvents = true; - } - else - { - Source = null; - error = $"Could not load image '{expandedUrl}'"; - return false; - } - - if (Source != null) - { - if (bgColor.A != 0) - { - Source = ReplaceTransparency(Source, bgColor); - BgColor = bgColor; - } - } - - Url = imageUrl; - } - catch (Exception ex) - { - Source = null; - error = ex.Message; - return false; - } - this.Scale = scale; - return true; - } - - public override string ToString() - { - return Url; - } - - private static BitmapFrame ReplaceTransparency(ImageSource bitmap, Color color) - { - var rect = new Rect(0, 0, (int)bitmap.Width, (int)bitmap.Height); - var visual = new DrawingVisual(); - var context = visual.RenderOpen(); - context.DrawRectangle(new SolidColorBrush(color), null, rect); - context.DrawImage(bitmap, rect); - context.Close(); - - var render = new RenderTargetBitmap((int)bitmap.Width, (int)bitmap.Height, - 96, 96, PixelFormats.Pbgra32); - render.Render(visual); - return BitmapFrame.Create(render); - } - } -} diff --git a/ImageCommentsExtension/MyLineTransformSource.cs b/ImageCommentsExtension/MyLineTransformSource.cs index ed7437a..0af5728 100644 --- a/ImageCommentsExtension/MyLineTransformSource.cs +++ b/ImageCommentsExtension/MyLineTransformSource.cs @@ -28,7 +28,7 @@ LineTransform ILineTransformSource.GetLineTransform(ITextViewLine line, double y if (_manager.Images.ContainsKey(lineNumber) && ImageAdornmentManager.Enabled) { double defaultHeight = line.DefaultLineTransform.BottomSpace; - MyImage image = _manager.Images[lineNumber]; + CommentImage image = _manager.Images[lineNumber]; lineTransform = new LineTransform(0, image.Height + defaultHeight, 1.0); imageOnLine = true; diff --git a/ImageCommentsExtension/MyLineTransformSourceProvider.cs b/ImageCommentsExtension/MyLineTransformSourceProvider.cs index ea68f01..2931ee2 100644 --- a/ImageCommentsExtension/MyLineTransformSourceProvider.cs +++ b/ImageCommentsExtension/MyLineTransformSourceProvider.cs @@ -1,5 +1,4 @@ using System.ComponentModel.Composition; -using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudio.Text.Formatting; @@ -11,6 +10,7 @@ namespace LM.ImageComments.EditorComponent ContentType("CSharp"), ContentType("C/C++"), ContentType("Basic"), + ContentType("code++.F#"), ContentType("F#"), ContentType("JScript"), ContentType("Python") diff --git a/ImageCommentsExtension/UIMessage.cs b/ImageCommentsExtension/UIMessage.cs index 02e4313..5dbb2da 100644 --- a/ImageCommentsExtension/UIMessage.cs +++ b/ImageCommentsExtension/UIMessage.cs @@ -22,8 +22,7 @@ public static void Show(string message) IVsOutputWindow outWindow = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow; Guid debugPaneGuid = VSConstants.GUID_OutWindowDebugPane; - IVsOutputWindowPane debugPane; - int gotPane = outWindow.GetPane(ref debugPaneGuid, out debugPane); + int gotPane = outWindow.GetPane(ref debugPaneGuid, out var debugPane); if (gotPane == VSConstants.S_OK) { debugPane.OutputString("[ImageComments Extension] " + message + "\n"); diff --git a/ImageCommentsExtension/VariableExpander.cs b/ImageCommentsExtension/VariableExpander.cs index d08deaf..8f69b3e 100644 --- a/ImageCommentsExtension/VariableExpander.cs +++ b/ImageCommentsExtension/VariableExpander.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; using System.Text.RegularExpressions; using EnvDTE; using EnvDTE80; @@ -21,7 +18,7 @@ namespace LM.ImageComments /// $(SolutionDir) /// $(ItemDir) /// - class VariableExpander + public class VariableExpander { private readonly Regex _variableMatcher; private const string VARIABLE_PATTERN = @"\$\(\S+?\)"; @@ -39,11 +36,7 @@ public VariableExpander(IWpfTextView view) { ThreadHelper.ThrowIfNotOnUIThread(); - if (view == null) - { - throw new ArgumentNullException("view"); - } - _view = view; + _view = view ?? throw new ArgumentNullException(nameof(view)); _variableMatcher = new Regex(VARIABLE_PATTERN, RegexOptions.Compiled); try @@ -85,8 +78,7 @@ private string evaluator(Match match) } else if (string.Compare(variableName, ITEMDIR_PATTERN, StringComparison.InvariantCultureIgnoreCase) == 0) { - ITextDocument document; - if (_view.TextDataModel.DocumentBuffer.Properties.TryGetProperty(typeof(ITextDocument), out document)) + if (_view.TextDataModel.DocumentBuffer.Properties.TryGetProperty(typeof(ITextDocument), out ITextDocument document)) { return Path.GetDirectoryName(document.FilePath); } @@ -113,8 +105,8 @@ private void populateVariableValues() _projectDirectory = ""; _solutionDirectory = ""; - ITextDocument document; - _view.TextDataModel.DocumentBuffer.Properties.TryGetProperty(typeof (ITextDocument), out document); + + _view.TextDataModel.DocumentBuffer.Properties.TryGetProperty(typeof (ITextDocument), out ITextDocument document); var dte2 = (DTE2)Package.GetGlobalService(typeof (SDTE)); ProjectItem projectItem = dte2.Solution.FindProjectItem(document.FilePath); diff --git a/ImageCommentsPackage/ImageCommentsPackage.csproj b/ImageCommentsPackage/ImageCommentsPackage.csproj index 948c906..ea3379b 100644 --- a/ImageCommentsPackage/ImageCommentsPackage.csproj +++ b/ImageCommentsPackage/ImageCommentsPackage.csproj @@ -445,15 +445,7 @@ - - - - ..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll - True - True - - - + diff --git a/ImageCommentsPackage/source.extension.vsixmanifest b/ImageCommentsPackage/source.extension.vsixmanifest index 75a6c13..88ea4d2 100644 --- a/ImageCommentsPackage/source.extension.vsixmanifest +++ b/ImageCommentsPackage/source.extension.vsixmanifest @@ -1,31 +1,31 @@  - - - ImageComments + + + ImageComments - Use images in code comments within Python, C#, C, C++, F#, DocFX and VB source files. + Use images in code comments within Python, C#, C, C++, F#, DocFX and VB source files. Written by Luke McQuade. Contributions by Lionsoft, Oleg K, Thomas P, Mark L. (This fork published by Thomas Pollak) - https://github.com/TomSmartBishop/image-comments - License.txt - img_cmt.png - image folding - - - - - - - - - - - - - - - - - + https://github.com/TomSmartBishop/image-comments + License.txt + img_cmt.png + image folding + + + + + + + + + + + + + + + + + diff --git a/Output/ImageComments.vsix b/Output/ImageComments.vsix index cca8e69..bbad953 100644 Binary files a/Output/ImageComments.vsix and b/Output/ImageComments.vsix differ diff --git a/Readme.md b/Readme.md index 537a9df..47ef612 100644 --- a/Readme.md +++ b/Readme.md @@ -1 +1 @@ -# ImageComments (a Visual Studio Extension) This fork is an updated version with all sound patches from the different forks out there. all sound patches from the different forks out there. ## Overview This is an extension for the Visual Studio code editor that allows images to be displayed amongst code, allowing for visually rich comments. For example... ![](http://lukesdm.github.com/image-comments/media/example-1.png) ## Usage Info ### Preamble Disclaimer: This project is a WIP and it's pretty rough around the edges. Please report issues on the GitHub repo. Supported Visual Studio Verions 2010~2015 (2017 warns about missing support, but works) ### Download/Installation [Latest 1.1.4.3](https://github.com/TomSmartBishop/image-comments/raw/master/Output/ImageComments.vsix) To install double-click/activate the VSIX file. ### How to use Image-comments are declared with: `/// ` or `/// ` The `scale` attribute multiplies the source width and height by Y and is optional. `// ` You can also specify the `bgcolor` attribute to set a background color for pictures with transparent areas, the color is specified as hex number: RRGGBB. You can use the VS environment variables $(ProjectDir), $(SolutionDir), and $(ItemDir) in URLs, e.g.: `# ` From 1.1.4.3 on you can also use relative paths (relative to the source file). Images from URL will be downloaded and not updated as long as the file exists in the user temp directory. Please note, if you use docfx at the same time, these environment variables must not be used, because docfx expects the images in a directory, e.g. "./images". Images are displayed using the [WPF Image control](http://msdn.microsoft.com/en-us/library/ms610982) with a [BitmapFrame](http://msdn.microsoft.com/en-us/library/ms619213) source, and accepted image and URL formats are tied to those, e.g. BMP, PNG, JPG all work as image formats, and C:\Path\To\Image.png, http://www.server.com/image.png and \\\server\folder\image.png all work as URLs. If there's a problem trying to load the image or parse the tag, the tag will be squiggly-underlined and hovering over this will show the error, e.g ![](http://lukesdm.github.com/image-comments/media/error-example-1.png) The languages currently supported are Python, C#, F# (fixme), C, C++ and VB. Image-comments don't really have anything to do with XML comments, but the format is convenient and it should be pretty straight-forward to transform them for Sandcastle documentation creation. The extension adds a command in the Tools menu to toggle image-comment display on or off. ### Uninstallation In VS, open the Extension Manager, select ImageComments, then click uninstall. A restart of VS is required. ### Some known issues * After adding an image-comment using a local image, you can't edit the image until VS is closed. * The caret/selection highlight height on image-comment lines grows as high the image. * You need to scroll/'bump' the editor window to see the effect of the on/off toggle command. ## Development Info Requires: Visual Studio 2015 SDK ### Build instructions Providing the VS SDK is installed, you should be able to build by opening the solution and hitting F6. Debugging has to be configured manually - On the Project Properties->Debug tab, choose 'Start External Program' and command line e.g. (if using default install location) 'C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe' with arguments '/rootsuffix Exp'. The 'Release' build configuration copies the .vsix package into the Solution's .\Output directory. ### Program structure It's a very small project and may be fairly self explanatory if you are familiar with Visual Studio editor extensions. There are two components to the extension: * ImageCommentsEditorComponent. Contains 97% of the functionality. * ImageCommentsPackage. Adds a command to enable/disable functionality; VSIX definition. For testing information, see .\Testing\Testing.html ### Some known implementation issues The code is a bit rough - it may not need a rewrite from scratch, but there's a bunch of stuff to be done * Images downloaded from an URL might not show up right away * Error/exception handling should be improved * Program/project structure could be improved * No automated tests and manual testing has been limited. * There are some fairly obvious potential optimisations, but so far performance impact on plain Visual Studio seems minimal (in a release build on a 1.4GHz Core 2 Duo laptop with 1GB RAM). It would probably just add unneccessary complexity, but further testing might show otherwise. * ... ## License Eclipse Public License v1.0. See [license text](http://github.com/lukesdm/image-comments/raw/master/License.txt) for details. ## Author The original plugin was made by Luke McQuade, this fork is maintained by Thomas Pollak. Further contributors: Lionsoft, Oleg Kosmakov, Morten Engelhardt Olsen, Wolfgang Kleinschmit, Sören Nils Kuklau, Tim Long \ No newline at end of file +# ImageComments (a Visual Studio Extension) This fork is an updated version with all sound patches from the different forks out there. all sound patches from the different forks out there. ## Overview This is an extension for the Visual Studio code editor that allows images to be displayed amongst code, allowing for visually rich comments. For example... ![](http://lukesdm.github.com/image-comments/media/example-1.png) ## Usage Info ### Preamble Disclaimer: This project is a WIP and it's pretty rough around the edges. Please report issues on the GitHub repo. Supported Visual Studio Verions 2010~2015 (2017 warns about missing support, but works) ### Download/Installation [Latest 1.1.4.4](https://github.com/TomSmartBishop/image-comments/raw/master/Output/ImageComments.vsix) To install double-click/activate the VSIX file. ### How to use Image-comments are declared with: `/// ` or `/// ` The optional `scale` attribute multiplies the source width and height (ranges from 0.01 to 100). Since 1.1.4.4 scaled images are anti aliased. The optional `opacity` attribute is available since 1.1.4.4 (from 0.0 to 1.0): `/// ` `// ` You can also specify the `bgcolor` attribute to set a background color for pictures with transparent areas, the color is specified as hex number: RRGGBB or RRGGBBAA, optionally you can use a short hand notation RGB. You can use the VS environment variables $(ProjectDir), $(SolutionDir), and $(ItemDir) in URLs, e.g.: `# ` From 1.1.4.3 on you can also use relative paths (relative to the source file). Images from URL will be downloaded and not updated as long as the file exists in the user temp directory. Please note, if you use docfx at the same time, these environment variables must not be used, because docfx expects the images in a directory, e.g. "./images". Images are displayed using the [WPF Image control](http://msdn.microsoft.com/en-us/library/ms610982) with a [BitmapFrame](http://msdn.microsoft.com/en-us/library/ms619213) source, and accepted image and URL formats are tied to those, e.g. BMP, PNG, JPG all work as image formats, and C:\Path\To\Image.png, http://www.server.com/image.png and \\\server\folder\image.png all work as URLs. If there's a problem trying to load the image or parse the tag, the tag will be squiggly-underlined and hovering over this will show the error, e.g ![](http://lukesdm.github.com/image-comments/media/error-example-1.png) The languages currently supported are Python, C#, F# (fixme), C, C++ and VB. Image-comments don't really have anything to do with XML comments, but the format is convenient and it should be pretty straight-forward to transform them for Sandcastle documentation creation. The extension adds a command in the Tools menu to toggle image-comment display on or off. ### Uninstallation In VS, open the Extension Manager, select ImageComments, then click uninstall. A restart of VS is required. ### Some known issues * After adding an image-comment using a local image, you can't edit the image until VS is closed. * The caret/selection highlight height on image-comment lines grows as high the image. * You need to scroll/'bump' the editor window to see the effect of the on/off toggle command. ## Development Info Requires: Visual Studio 2015 SDK ### Build instructions Providing the VS SDK is installed, you should be able to build by opening the solution and hitting F6. Debugging has to be configured manually - On the Project Properties->Debug tab, choose 'Start External Program' and command line e.g. (if using default install location) 'C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe' with arguments '/rootsuffix Exp'. The 'Release' build configuration copies the .vsix package into the Solution's .\Output directory. ### Program structure It's a very small project and may be fairly self explanatory if you are familiar with Visual Studio editor extensions. There are two components to the extension: * ImageCommentsEditorComponent. Contains 97% of the functionality. * ImageCommentsPackage. Adds a command to enable/disable functionality; VSIX definition. For testing information, see .\Testing\Testing.html ### Some known implementation issues The code is a bit rough - it may not need a rewrite from scratch, but there's a bunch of stuff to be done * Images downloaded from an URL might not show up right away * Error/exception handling should be improved * Program/project structure could be improved * No automated tests and manual testing has been limited. * There are some fairly obvious potential optimisations, but so far performance impact on plain Visual Studio seems minimal (in a release build on a 1.4GHz Core 2 Duo laptop with 1GB RAM). It would probably just add unneccessary complexity, but further testing might show otherwise. * ... ## License Eclipse Public License v1.0. See [license text](http://github.com/lukesdm/image-comments/raw/master/License.txt) for details. ## Author The original plugin was made by Luke McQuade, this fork is maintained by Thomas Pollak. Further contributors: Lionsoft, Oleg Kosmakov, Morten Engelhardt Olsen, Wolfgang Kleinschmit, Sören Nils Kuklau, Tim Long \ No newline at end of file diff --git a/Testing/DummySolution/DummyMain/TestFile.cs b/Testing/DummySolution/DummyMain/TestFile.cs index 9538dd5..7b0a4b3 100644 --- a/Testing/DummySolution/DummyMain/TestFile.cs +++ b/Testing/DummySolution/DummyMain/TestFile.cs @@ -16,7 +16,7 @@ public Signal ReconstituteSignal(double a0, double[,] a, double[,] b, double[] x /// /// Gets point of intersection of line and plane. 3 cases to consider. - /// + /// /// public Point LinePlaneIntersecter(Plane plane, Line line) { diff --git a/Testing/DummySolution/DummyMain/TestFile_2.cs b/Testing/DummySolution/DummyMain/TestFile_2.cs index 1d951ef..3c60daf 100644 --- a/Testing/DummySolution/DummyMain/TestFile_2.cs +++ b/Testing/DummySolution/DummyMain/TestFile_2.cs @@ -2,7 +2,7 @@ namespace Parties { public class PhotoParty { - /// + /// public static void Penguins() { ; @@ -17,7 +17,7 @@ public static void Koala() } /// - /// + /// /// public static void Misc() { diff --git a/Testing/DummySolution/DummyVB/Module1.vb b/Testing/DummySolution/DummyVB/Module1.vb index e52b97b..0473a6a 100644 --- a/Testing/DummySolution/DummyVB/Module1.vb +++ b/Testing/DummySolution/DummyVB/Module1.vb @@ -2,7 +2,8 @@ ''' ''' ''' -''' +''' + Module Module1 Sub Main() diff --git a/logo.pdn b/logo.pdn new file mode 100644 index 0000000..a83b31b Binary files /dev/null and b/logo.pdn differ diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..152ccfc Binary files /dev/null and b/logo.png differ