diff options
author | Matt Ward <matt.ward@microsoft.com> | 2018-03-26 11:43:28 +0300 |
---|---|---|
committer | Matt Ward <matt.ward@microsoft.com> | 2018-03-26 11:43:28 +0300 |
commit | 6c1c012b34afc411d99bda52a09cececb3e20666 (patch) | |
tree | 6cf209a60dc27806db81af78cb1997fd612df919 /main/src/core | |
parent | 3707fcf78ead900e97acb4b160c33739c43259a9 (diff) | |
parent | 2014698650693b56a4a213e1653c2af98fb0ce11 (diff) |
Merge branch 'master' into xamarin-forms-use-dotnet-templating-project-templates
Diffstat (limited to 'main/src/core')
168 files changed, 4306 insertions, 1229 deletions
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Shared.projitems b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Shared.projitems index 957dd9b2eb..003ef36856 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Shared.projitems +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Shared.projitems @@ -79,6 +79,7 @@ <Compile Include="$(MSBuildThisFileDirectory)Mono.TextEditor\UrlMarker.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Mono.TextEditor\TextEditorData.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Mono.TextEditor\DefaultIndentationTracker.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Mono.TextEditor\CaretImpl.ITextCaret.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)Mono.TextEditor\" />
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/CompressingTreeList.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/CompressingTreeList.cs index 068763df76..60a86146a0 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/CompressingTreeList.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/CompressingTreeList.cs @@ -37,7 +37,7 @@ namespace Mono.TextEditor.Utils internal class CompressingNode : IRedBlackTreeNode { - internal readonly T value; + internal T value; internal int count, totalCount; public CompressingNode (T value, int count) @@ -311,7 +311,11 @@ namespace Mono.TextEditor.Utils return GetNode (ref index).value; } set { - RemoveAt (index); + if (index < Count) { + RemoveAt (index); + } else { + InsertRange (Count, index - Count, default (T)); + } Insert (index, value); } } diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/Diff.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/Diff.cs index 452719762c..9b532cca71 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/Diff.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/Diff.cs @@ -96,6 +96,7 @@ using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; +using MonoDevelop.Core; namespace Mono.TextEditor.Utils { @@ -486,7 +487,7 @@ namespace Mono.TextEditor.Utils if (diff == null) return ""; - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); IEnumerator<Hunk> he = diff.GetEnumerator (); he.MoveNext (); @@ -533,7 +534,7 @@ namespace Mono.TextEditor.Utils sb.Append ("@@ -").Append (remStart).Append (",").Append (remEnd - remStart).Append (" +").Append (insStart).Append (",").Append (insEnd - insStart).AppendLine (" @@"); WriteHunks (qh, baseDocument, changedDocument, sb); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/HtmlWriter.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/HtmlWriter.cs index 0b767362dd..ec69683c7a 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/HtmlWriter.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/HtmlWriter.cs @@ -49,7 +49,7 @@ namespace Mono.TextEditor.Utils public static string GenerateHtml (List<List<ClipboardColoredText>> chunks, EditorTheme style, ITextEditorOptions options, bool includeBoilerplate = true) { - var htmlText = new StringBuilder (); + var htmlText = StringBuilderCache.Allocate (); if (includeBoilerplate) { htmlText.AppendLine (@"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Transitional//EN"">"); htmlText.AppendLine ("<HTML>"); @@ -100,7 +100,7 @@ namespace Mono.TextEditor.Utils if (Platform.IsWindows) return GenerateCFHtml (htmlText.ToString ()); - return htmlText.ToString (); + return StringBuilderCache.ReturnAndFree (htmlText); } static readonly string emptyCFHtmlHeader = GenerateCFHtmlHeader (0, 0, 0, 0); diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RedBlackTree.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RedBlackTree.cs index f75e298da7..0ac5ecbe76 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RedBlackTree.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RedBlackTree.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; +using MonoDevelop.Core; namespace Mono.TextEditor.Utils { @@ -510,9 +511,9 @@ namespace Mono.TextEditor.Utils { if (Root == null) return "<null>"; - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); AppendNode (result, Root, 0); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } } } diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RtfWriter.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RtfWriter.cs index 6a62622f19..4a5e01b847 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RtfWriter.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RtfWriter.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using Mono.TextEditor.Highlighting; using MonoDevelop.Core.Text; using MonoDevelop.Ide; +using MonoDevelop.Core; namespace Mono.TextEditor.Utils { @@ -36,7 +37,7 @@ namespace Mono.TextEditor.Utils { static string CreateColorTable (List<Cairo.Color> colorList) { - var colorTable = new StringBuilder (); + var colorTable = StringBuilderCache.Allocate (); colorTable.Append (@"{\colortbl ;"); for (int i = 0; i < colorList.Count; i++) { var color = colorList [i]; @@ -49,7 +50,7 @@ namespace Mono.TextEditor.Utils colorTable.Append (";"); } colorTable.Append ("}"); - return colorTable.ToString (); + return StringBuilderCache.ReturnAndFree (colorTable); } public static string GenerateRtf (TextEditorData data) @@ -97,7 +98,7 @@ namespace Mono.TextEditor.Utils internal static string GenerateRtf (List<List<ClipboardColoredText>> chunks, MonoDevelop.Ide.Editor.Highlighting.EditorTheme style, ITextEditorOptions options) { - var rtfText = new StringBuilder (); + var rtfText = StringBuilderCache.Allocate (); var colorList = new List<Cairo.Color> (); bool isItalic = false; @@ -131,7 +132,7 @@ namespace Mono.TextEditor.Utils rtfText.AppendLine (@"\line"); } - var rtf = new StringBuilder(); + var rtf = StringBuilderCache.Allocate (); rtf.AppendLine (@"{\rtf1\ansi\deff0\adeflang1025"); rtf.AppendLine (@"{\fonttbl"); @@ -147,9 +148,9 @@ namespace Mono.TextEditor.Utils rtf.Append (fontSize); } catch (Exception) {}; rtf.AppendLine (@"\cf1"); - rtf.Append (rtfText.ToString ()); + rtf.Append (StringBuilderCache.ReturnAndFree (rtfText)); rtf.Append("}"); - return rtf.ToString (); + return StringBuilderCache.ReturnAndFree (rtf); } } } diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Vi/ViActions.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Vi/ViActions.cs index 9b8c9d1df0..17eb2a7457 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Vi/ViActions.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Vi/ViActions.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Text; +using MonoDevelop.Core; using MonoDevelop.Core.Text; using MonoDevelop.Ide.Editor; @@ -121,7 +122,7 @@ namespace Mono.TextEditor.Vi DocumentLine seg = data.Document.GetLine (startLine); startOffset = seg.Offset; - StringBuilder sb = new StringBuilder (data.Document.GetTextAt (seg).TrimEnd ()); + StringBuilder sb = StringBuilderCache.Allocate (data.Document.GetTextAt (seg).TrimEnd ()); //lastSpaceOffset = startOffset + sb.Length; for (int i = startLine + 1; i <= endLine; i++) { @@ -132,7 +133,7 @@ namespace Mono.TextEditor.Vi } length = (seg.Offset - startOffset) + seg.Length; // TODO: handle conversion issues ? - data.Replace (startOffset, length, sb.ToString ()); + data.Replace (startOffset, length, StringBuilderCache.ReturnAndFree (sb)); } public static void ToggleCase (TextEditorData data) @@ -141,7 +142,7 @@ namespace Mono.TextEditor.Vi if (!data.CanEditSelection) return; - StringBuilder sb = new StringBuilder (data.SelectedText); + StringBuilder sb = StringBuilderCache.Allocate (data.SelectedText); for (int i = 0; i < sb.Length; i++) { char ch = sb [i]; if (Char.IsLower (ch)) @@ -149,7 +150,7 @@ namespace Mono.TextEditor.Vi else if (Char.IsUpper (ch)) sb [i] = Char.ToLower (ch); } - data.Replace (data.SelectionRange.Offset, data.SelectionRange.Length, sb.ToString ()); + data.Replace (data.SelectionRange.Offset, data.SelectionRange.Length, StringBuilderCache.ReturnAndFree (sb)); } else if (data.CanEdit (data.Caret.Line)) { char ch = data.Document.GetCharAt (data.Caret.Offset); if (Char.IsLower (ch)) diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/CaretMoveActions.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/CaretMoveActions.cs index ff898c56b5..c00fd946c2 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/CaretMoveActions.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/CaretMoveActions.cs @@ -42,6 +42,9 @@ namespace Mono.TextEditor { static class CaretMoveActions { + internal const ushort HighSurrogateMarker = 0b1101_1000_0000_0000; + internal const ushort LowSurrogateMarker = 0b1101_1100_0000_0000; + public static void Left (TextEditorData data) { using (var undo = data.OpenUndoGroup ()) { @@ -88,6 +91,10 @@ namespace Mono.TextEditor nextLocation = new DocumentLocation (data.Caret.Line - 1, data.GetVirtualIndentationColumn (nextLocation)); data.Caret.Location = nextLocation; } + var curOffset = data.Caret.Offset; + if (curOffset > 0 && curOffset < data.Length && ((ushort)data.GetCharAt (curOffset) & LowSurrogateMarker) == LowSurrogateMarker) + data.Caret.Offset--; + } } @@ -104,7 +111,7 @@ namespace Mono.TextEditor data.Caret.Offset = data.FindPrevSubwordOffset (data.Caret.Offset); } } - + public static void Right (TextEditorData data) { using (var undo = data.OpenUndoGroup ()) { @@ -113,9 +120,8 @@ namespace Mono.TextEditor data.ClearSelection (); return; } - DocumentLine line = data.Document.GetLine (data.Caret.Line); - IEnumerable<FoldSegment > foldings = data.Document.GetStartFoldings (line); + IEnumerable<FoldSegment> foldings = data.Document.GetStartFoldings (line); FoldSegment segment = null; foreach (FoldSegment folding in foldings) { if (folding.IsCollapsed && folding.Offset == data.Caret.Offset) { @@ -124,7 +130,7 @@ namespace Mono.TextEditor } } if (segment != null) { - data.Caret.Offset = segment.EndOffset; + data.Caret.Offset = segment.EndOffset; return; } @@ -156,9 +162,17 @@ namespace Mono.TextEditor } } } + MoveOutOfUTF32Character (data); } } - + + static void MoveOutOfUTF32Character (TextEditorData data) + { + var curOffset = data.Caret.Offset; + if (curOffset > 0 && curOffset < data.Length && ((ushort)data.GetCharAt (curOffset) & HighSurrogateMarker) == HighSurrogateMarker) + data.Caret.Offset++; + } + public static void NextWord (TextEditorData data) { using (var undo = data.OpenUndoGroup ()) { @@ -185,6 +199,7 @@ namespace Mono.TextEditor data.ClearSelection (); data.Caret.Location = (line >= DocumentLocation.MinLine) ? new DocumentLocation (line, col) : new DocumentLocation (DocumentLocation.MinLine, DocumentLocation.MinColumn); data.Caret.SetToDesiredColumn (desiredColumn); + MoveOutOfUTF32Character (data); return; } @@ -196,6 +211,7 @@ namespace Mono.TextEditor } else { ToDocumentStart (data); } + MoveOutOfUTF32Character (data); } } @@ -237,6 +253,7 @@ namespace Mono.TextEditor } else { data.Caret.Offset = data.Document.Length; } + MoveOutOfUTF32Character (data); return; } @@ -248,6 +265,7 @@ namespace Mono.TextEditor } else { ToDocumentEnd (data); } + MoveOutOfUTF32Character (data); } } diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs index f87a6d2bf4..c0894e600b 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs @@ -86,7 +86,7 @@ namespace Mono.TextEditor string GetCopiedPlainText (string eol = "\n") { - var plainText = new StringBuilder (); + var plainText = StringBuilderCache.Allocate (); bool first = true; foreach (var line in copiedColoredChunks) { if (!first) { @@ -99,7 +99,7 @@ namespace Mono.TextEditor plainText.Append (chunk.Text); } } - return plainText.ToString (); + return StringBuilderCache.ReturnAndFree (plainText); } public void SetData (SelectionData selection_data, uint info) @@ -435,12 +435,17 @@ namespace Mono.TextEditor data.Document.CommitLineUpdate (data.GetLineByOffset (insertionOffset)); return result; } - + public static void Paste (TextEditorData data) { + PasteWithResult (data); + } + + public static bool PasteWithResult (TextEditorData data) + { if (!data.CanEditSelection) - return; - PasteFrom (Clipboard.Get (CopyOperation.CLIPBOARD_ATOM), data, false, data.IsSomethingSelected ? data.SelectionRange.Offset : data.Caret.Offset); + return false; + return PasteFrom (Clipboard.Get (CopyOperation.CLIPBOARD_ATOM), data, false, data.IsSomethingSelected ? data.SelectionRange.Offset : data.Caret.Offset) > 0; } public static string GetClipboardContent() diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/DeleteActions.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/DeleteActions.cs index 83c0cbf553..2e061396f2 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/DeleteActions.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/DeleteActions.cs @@ -315,7 +315,12 @@ namespace Mono.TextEditor if (offset <= 0) return; var version = data.Version; - data.Remove (offset - 1, 1); + var o = data.Caret.Offset; + if (((ushort)data.GetCharAt (offset - 1) & CaretMoveActions.LowSurrogateMarker) == CaretMoveActions.LowSurrogateMarker) { + data.Remove (offset - 2, 2); + } else { + data.Remove (offset - 1, 1); + } data.Caret.Location = data.OffsetToLocation (version.MoveOffsetTo (data.Version, offset)); } @@ -380,7 +385,12 @@ namespace Mono.TextEditor line.UnicodeNewline = UnicodeNewline.Unknown; } } else { - data.Remove (data.Caret.Offset, 1); + var o = data.Caret.Offset; + if (((ushort)data.GetCharAt (o) & CaretMoveActions.HighSurrogateMarker) == CaretMoveActions.HighSurrogateMarker) { + data.Remove (o, 2); + } else { + data.Remove (o, 1); + } data.Document.CommitLineUpdate (data.Caret.Line); } data.FixVirtualIndentation (); diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/MiscActions.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/MiscActions.cs index 5bf609cd3a..e38d528e9d 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/MiscActions.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/MiscActions.cs @@ -242,9 +242,16 @@ namespace Mono.TextEditor { using (var undo = data.OpenUndoGroup ()) { data.EnsureCaretIsNotVirtual (); + + var oldCaretLine = data.Caret.Location.Line; + string indentString = data.GetIndentationString (data.Caret.Location); data.InsertAtCaret (data.EolMarker); - data.InsertAtCaret (indentString); + + // Don't insert the indent string if the EOL insertion modified the caret location in an unexpected fashion + // (This likely means someone has custom logic regarding insertion of the EOL) + if (data.Caret.Location.Line == oldCaretLine + 1 && data.Caret.Location.Column == 1) + data.InsertAtCaret (indentString); } } @@ -365,6 +372,8 @@ namespace Mono.TextEditor //int relCaretOffset = data.Caret.Offset - startLine.Offset; Mono.TextEditor.DocumentLine prevLine = data.Document.GetLine (lineStart - 1); + if (prevLine == null) + return; string text = data.Document.GetTextAt (prevLine.Offset, prevLine.Length); List<TextLineMarker> prevLineMarkers = new List<TextLineMarker> (data.Document.GetMarkers (prevLine)); data.Document.ClearMarkers (prevLine); diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.ITextCaret.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.ITextCaret.cs new file mode 100644 index 0000000000..386a85f6c1 --- /dev/null +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.ITextCaret.cs @@ -0,0 +1,236 @@ +ο»Ώ// +// CaretImpl.ITextCaret.cs +// +// Author: +// Mike KrΓΌger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Linq; +using MonoDevelop.Core.Text; +using MonoDevelop.Ide.Editor; +using System; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Formatting; +using Mono.TextEditor; +using MonoDevelop.Ide.Editor; + +namespace Mono.TextEditor +{ + partial class CaretImpl : ITextCaret + { + MonoTextEditor TextEditor => TextEditorData.Parent; + + VirtualSnapshotPoint insertionPoint; + PositionAffinity _caretAffinity; + + ITextViewLine ITextCaret.ContainingTextViewLine => TextEditor.GetTextViewLineContainingBufferPosition (((ITextCaret)this).Position.VirtualBufferPosition.Position); + + double ITextCaret.Left => TextEditor.TextArea.TextViewMargin.caretX; + + double ITextCaret.Width => TextEditor.TextArea.TextViewMargin.charWidth; + + double ITextCaret.Right => TextEditor.TextArea.TextViewMargin.caretX + TextEditor.TextArea.TextViewMargin.charWidth; + + double ITextCaret.Top => TextEditor.TextArea.TextViewMargin.caretY; + + double ITextCaret.Height => TextEditor.LineHeight; + + double ITextCaret.Bottom => TextEditor.TextArea.TextViewMargin.caretY + TextEditor.LineHeight; + + CaretPosition ITextCaret.Position { + get { +#if TARGET_VS + //In theory the _insertion point is always at the same snapshot at the _wpfTextView but there could be cases + //where someone is using the position in a classifier that is using the caret position in the classificaiton changed event. + //In that case return the old insertion point. + return new CaretPosition(_insertionPoint, + _textView.BufferGraph.CreateMappingPoint(_insertionPoint.Position, PointTrackingMode.Positive), + _caretAffinity); +#else + // MD doesn't update the caret location until after the command has gone completely through the command chain. Too much VS stuff depends on it getting updated earlier. + // Thus, I'm going to ensure this is returning a position based on the current snapshot, ignoring/breaking the scenario outlined in the above comment. + VirtualSnapshotPoint insertionPointInLatestSnapshot = insertionPoint.TranslateTo (insertionPoint.Position.Snapshot.TextBuffer.CurrentSnapshot, PointTrackingMode.Positive); + return new CaretPosition (insertionPointInLatestSnapshot, + TextEditor.BufferGraph.CreateMappingPoint (insertionPointInLatestSnapshot.Position, PointTrackingMode.Positive), + _caretAffinity); +#endif + } + } + + + bool ITextCaret.OverwriteMode => !IsInInsertMode; + + bool ITextCaret.InVirtualSpace { + get { + var snapshotLine = TextEditor.TextBuffer.CurrentSnapshot.GetLineFromPosition (((ITextCaret)this).Position.BufferPosition); + + return TextEditor.Caret.Column > snapshotLine.Length + 1; + } + } + + bool ITextCaret.IsHidden { + get { + return !IsVisible; + } + set { + IsVisible = !value; + } + } + + DocumentLocation oldCaretLocation; + void PositionChanged_ITextCaret (CaretLocationEventArgs args) + { + //Some unit tests don't initialize full UI representation of MonoTextEditor + //which means they don't depend on ITextCaret implementation, so we can return here + //If something is using MonoTextEditor directly(e.g. DiffView) and is not initializing ITextView + //TextBuffer is null, in that case don't depend on ITextCaret implementation, so we can return here + if (TextEditor?.TextBuffer == null) + return; + // MD doesn't fire textEditor.CaretPositionChanged until after the command has gone completely through the command chain. + // Too much VS stuff depends on it getting updated earlier, so we'll use this event which fires earlier. + int position = TextEditor.Caret.Offset; + VirtualSnapshotPoint vsp = new VirtualSnapshotPoint (TextEditor.TextSnapshot, position); + + insertionPoint = vsp; + if (args.CaretChangeReason == CaretChangeReason.Movement) { + oldCaretLocation = args.Location; + var oldOffset = TextEditor.LocationToOffset (args.Location); + var snapshotPoint = new SnapshotPoint (TextEditor.TextSnapshot, oldOffset); + var mappingPoint = TextEditor.BufferGraph.CreateMappingPoint (snapshotPoint, PointTrackingMode.Positive); + var oldCaretPosition = new CaretPosition (vsp, mappingPoint, _caretAffinity); + var eventArgs = new CaretPositionChangedEventArgs (TextEditor, oldCaretPosition, ((ITextCaret)this).Position); + + ITextCaret_PositionChanged?.Invoke (this, eventArgs); + } + } + + event EventHandler<CaretPositionChangedEventArgs> ITextCaret_PositionChanged; + event EventHandler<CaretPositionChangedEventArgs> ITextCaret.PositionChanged { + add { ITextCaret_PositionChanged += value; } + remove { ITextCaret_PositionChanged -= value; } + } + + void ITextCaret.EnsureVisible () + { + TextEditor.ScrollToCaret (); + } + + + CaretPosition ITextCaret.MoveTo (ITextViewLine textLine, double xCoordinate) + { + Line = textLine.Start; + // TODO: xCoordinate - is that just visual or does it have an impact on the column ? + return ((ITextCaret)this).Position; + } + + CaretPosition ITextCaret.MoveTo (ITextViewLine textLine, double xCoordinate, bool captureHorizontalPosition) + { + Line = textLine.Start; + // TODO: xCoordinate - is that just visual or does it have an impact on the column ? + return ((ITextCaret)this).Position; + } + + CaretPosition ITextCaret.MoveTo (ITextViewLine textLine) + { + Line = textLine.Start; + return ((ITextCaret)this).Position; + } + + CaretPosition ITextCaret.MoveTo (SnapshotPoint bufferPosition) + { + this.InternalMoveTo (new VirtualSnapshotPoint (bufferPosition), PositionAffinity.Successor, true, true, true); + + return ((ITextCaret)this).Position; + } + + CaretPosition ITextCaret.MoveTo (SnapshotPoint bufferPosition, PositionAffinity caretAffinity) + { + this.InternalMoveTo (new VirtualSnapshotPoint (bufferPosition), caretAffinity, true, true, true); + + return ((ITextCaret)this).Position; + } + + CaretPosition ITextCaret.MoveTo (SnapshotPoint bufferPosition, PositionAffinity caretAffinity, bool captureHorizontalPosition) + { + this.InternalMoveTo (new VirtualSnapshotPoint (bufferPosition), caretAffinity, captureHorizontalPosition, true, true); + + return ((ITextCaret)this).Position; + } + + CaretPosition ITextCaret.MoveTo (VirtualSnapshotPoint bufferPosition) + { + this.InternalMoveTo (bufferPosition, PositionAffinity.Successor, true, true, true); + + return ((ITextCaret)this).Position; + } + + CaretPosition ITextCaret.MoveTo (VirtualSnapshotPoint bufferPosition, PositionAffinity caretAffinity) + { + this.InternalMoveTo (bufferPosition, caretAffinity, true, true, true); + + return ((ITextCaret)this).Position; + } + + CaretPosition ITextCaret.MoveTo (VirtualSnapshotPoint bufferPosition, PositionAffinity caretAffinity, bool captureHorizontalPosition) + { + this.InternalMoveTo (bufferPosition, caretAffinity, captureHorizontalPosition, true, true); + + return ((ITextCaret)this).Position; + } + + CaretPosition ITextCaret.MoveToNextCaretPosition () + { + // TODO: Implement me - not sure if we've a 'next' position. What should this be ? + return ((ITextCaret)this).Position; + } + + CaretPosition ITextCaret.MoveToPreferredCoordinates () + { + TextEditor.GetTextEditorData ().FixVirtualIndentation (); + return ((ITextCaret)this).Position; + } + + CaretPosition ITextCaret.MoveToPreviousCaretPosition () + { + Location = oldCaretLocation; + return ((ITextCaret)this).Position; + } + + void InternalMoveTo (VirtualSnapshotPoint bufferPosition, PositionAffinity caretAffinity, bool captureHorizontalPosition, bool captureVerticalPosition, bool raiseEvent) + { + int requestedPosition = bufferPosition.Position; + ITextSnapshotLine snapshotLine = TextEditor.TextSnapshot.GetLineFromPosition (requestedPosition); + int line = snapshotLine.LineNumber + 1; + + int col; + if (bufferPosition.IsInVirtualSpace) { + col = bufferPosition.VirtualSpaces; + } else { + col = requestedPosition - snapshotLine.Start + 1; + } + + TextEditor.SetCaretTo (line, col, false, false); + } + } +}
\ No newline at end of file diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs index d27baf33ae..9822b5a151 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/CaretImpl.cs @@ -27,12 +27,13 @@ using System; using System.Linq; +using Microsoft.VisualStudio.Text; using MonoDevelop.Core.Text; using MonoDevelop.Ide.Editor; namespace Mono.TextEditor { - class CaretImpl : MonoDevelop.Ide.Editor.Caret + partial class CaretImpl : MonoDevelop.Ide.Editor.Caret { bool isInInsertMode = true; bool autoScrollToCaret = true; @@ -196,6 +197,10 @@ namespace Mono.TextEditor AllowCaretBehindLineEnd = false; DesiredColumn = DocumentLocation.MinColumn; AutoUpdatePosition = true; + + // Set up initial values + _caretAffinity = PositionAffinity.Successor; + insertionPoint = new VirtualSnapshotPoint (new SnapshotPoint (editor.Document.TextBuffer.CurrentSnapshot, 0)); } /// <summary> @@ -324,6 +329,7 @@ namespace Mono.TextEditor { TextEditorData.Document.EnsureOffsetIsUnfolded (Offset); base.OnPositionChanged (args); + PositionChanged_ITextCaret (args); } protected virtual void OnModeChanged () diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DiffTracker.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DiffTracker.cs index d35ced6951..2246c19076 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DiffTracker.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DiffTracker.cs @@ -33,9 +33,13 @@ namespace Mono.TextEditor { class LineChangeInfo { + internal readonly static LineChangeInfo Unchanged = new LineChangeInfo (TextDocument.LineState.Unchanged); + internal readonly static LineChangeInfo Dirty = new LineChangeInfo (TextDocument.LineState.Dirty); + internal readonly static LineChangeInfo Changed = new LineChangeInfo (TextDocument.LineState.Changed); + public Mono.TextEditor.TextDocument.LineState state; - public LineChangeInfo (Mono.TextEditor.TextDocument.LineState state) + LineChangeInfo (Mono.TextEditor.TextDocument.LineState state) { this.state = state; } @@ -70,7 +74,9 @@ namespace Mono.TextEditor if (lineStates == null) return; for(int i = e.TextChanges.Count - 1; i >= 0; i--) {
- var change = e.TextChanges[i];
+ var change = e.TextChanges[i]; + if (change.RemovalLength == 0) + continue;
var startLine = trackDocument.GetLineByOffset (change.Offset); var endRemoveLine = trackDocument.GetLineByOffset (change.Offset + change.RemovalLength); if (startLine == null || endRemoveLine == null) @@ -91,17 +97,20 @@ namespace Mono.TextEditor var startLine = trackDocument.GetLineByOffset (change.NewOffset); var endLine = trackDocument.GetLineByOffset (change.NewOffset + change.InsertionLength); var lineNumber = startLine.LineNumber; - var oldState = lineNumber < lineStates.Count ? lineStates [lineNumber] : null; - if (oldState != null && oldState.state == TextDocument.LineState.Dirty) - continue; var insertedLines = endLine.LineNumber - lineNumber; + if (insertedLines == 0) { + var oldState = lineNumber < lineStates.Count ? lineStates [lineNumber] : null; + if (oldState != null && oldState.state == TextDocument.LineState.Dirty) + continue; + lineStates[lineNumber] = LineChangeInfo.Dirty; + if (trackDocument != null) + trackDocument.CommitMultipleLineUpdate (lineNumber, lineNumber + insertedLines); + continue; + } try { - lineStates [lineNumber] = new LineChangeInfo (Mono.TextEditor.TextDocument.LineState.Dirty); + lineStates.InsertRange (lineNumber, insertedLines , LineChangeInfo.Dirty); if (trackDocument != null) - trackDocument.CommitLineUpdate (lineNumber); - while (insertedLines-- > 0) { - lineStates.Insert (lineNumber, new LineChangeInfo (Mono.TextEditor.TextDocument.LineState.Dirty)); - } + trackDocument.CommitMultipleLineUpdate (lineNumber, lineNumber + insertedLines); } catch (Exception ex) { Console.WriteLine ("error while DiffTracker.TrackDocument_TextChanged:" + ex); } @@ -113,11 +122,11 @@ namespace Mono.TextEditor if (lineStates != null) { foreach (var node in lineStates.tree) { if (node.value.state == Mono.TextEditor.TextDocument.LineState.Dirty) - node.value.state = Mono.TextEditor.TextDocument.LineState.Changed; + node.value = LineChangeInfo.Changed; } } else { lineStates = new CompressingTreeList<LineChangeInfo>((x, y) => x.Equals(y)); - lineStates.InsertRange(0, document.LineCount + 1, new LineChangeInfo (Mono.TextEditor.TextDocument.LineState.Unchanged)); + lineStates.InsertRange (0, document.LineCount + 1, LineChangeInfo.Unchanged); trackDocument.TextChanging += TrackDocument_TextChanging; trackDocument.TextChanged += TrackDocument_TextChanged; } @@ -126,7 +135,7 @@ namespace Mono.TextEditor public void Reset () { lineStates = new CompressingTreeList<LineChangeInfo>((x, y) => x.Equals(y)); - lineStates.InsertRange(0, trackDocument.LineCount + 1, new LineChangeInfo (Mono.TextEditor.TextDocument.LineState.Unchanged)); + lineStates.InsertRange(0, trackDocument.LineCount + 1, LineChangeInfo.Unchanged); } } } diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DocumentLine.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DocumentLine.cs index d05fb34a1f..2ee0d26d59 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DocumentLine.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DocumentLine.cs @@ -32,6 +32,7 @@ using Mono.TextEditor.Highlighting; using System.Linq; using MonoDevelop.Ide.Editor; using MonoDevelop.Core.Text; +using MonoDevelop.Core; namespace Mono.TextEditor { @@ -175,7 +176,7 @@ namespace Mono.TextEditor /// </returns> public string GetIndentation (TextDocument doc) { - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); int offset = Offset; int max = System.Math.Min (offset + LengthIncludingDelimiter, doc.Length); for (int i = offset; i < max; i++) { @@ -184,7 +185,7 @@ namespace Mono.TextEditor break; result.Append (ch); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public int GetLogicalColumn (TextEditorData editor, int visualColumn) diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DocumentUpdateRequest.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DocumentUpdateRequest.cs index 2cd7c20c7c..dded9bdb91 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DocumentUpdateRequest.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DocumentUpdateRequest.cs @@ -84,12 +84,8 @@ namespace Mono.TextEditor public override void Update (MonoTextEditor editor) { if (start == end) { - editor.TextViewMargin.RemoveCachedLine (start); editor.RedrawLine (start); } else { - for (int i = start; i <= end; i++) { - editor.TextViewMargin.RemoveCachedLine (i); - } editor.RedrawLines (start, end); } } diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs index 3d39f9b743..05a1c1db71 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs @@ -35,6 +35,7 @@ using System.ComponentModel; using System.Text; using System.Threading.Tasks; using System.Threading; +using MonoDevelop.Ide.Composition;
using MonoDevelop.Core.Text; using MonoDevelop.Ide.Editor; using MonoDevelop.Core; @@ -42,6 +43,7 @@ using System.IO; using MonoDevelop.Ide.Editor.Highlighting; using Microsoft.VisualStudio.Platform; using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities;
namespace Mono.TextEditor {
@@ -69,7 +71,7 @@ namespace Mono.TextEditor return PlatformCatalog.Instance.MimeToContentTypeRegistryService.GetMimeType(snapshot.ContentType) ?? snapshot.ContentType.TypeName;
}
set {
- var newContentType = value != null ? GetContentTypeFromMimeType(value) : PlatformCatalog.Instance.ContentTypeRegistryService.UnknownContentType; + var newContentType = value != null ? GetContentTypeFromMimeType(null, value) : PlatformCatalog.Instance.ContentTypeRegistryService.UnknownContentType; if (this.TextBuffer.CurrentSnapshot.ContentType != newContentType) {
this.TextBuffer.ChangeContentType(newContentType, null);
@@ -77,9 +79,20 @@ namespace Mono.TextEditor }
}
- private static Microsoft.VisualStudio.Utilities.IContentType GetContentTypeFromMimeType(string mimeType)
+ private static Microsoft.VisualStudio.Utilities.IContentType GetContentTypeFromMimeType(string filePath, string mimeType)
{
- Microsoft.VisualStudio.Utilities.IContentType contentType = PlatformCatalog.Instance.MimeToContentTypeRegistryService.GetContentType(mimeType);
+ if (filePath != null) + {
+ IFilePathRegistryService filePathRegistryService = CompositionManager.GetExportedValue<IFilePathRegistryService> ();
+
+ IContentType contentTypeFromPath = filePathRegistryService.GetContentTypeForPath (filePath);
+ if (contentTypeFromPath != PlatformCatalog.Instance.ContentTypeRegistryService.UnknownContentType) + {
+ return contentTypeFromPath;
+ }
+ }
+
+ IContentType contentType = PlatformCatalog.Instance.MimeToContentTypeRegistryService.GetContentType (mimeType); if (contentType == null)
{
// fallback 1: see if there is a content tyhpe with the same name
@@ -147,7 +160,7 @@ namespace Mono.TextEditor void SyntaxMode_HighlightingStateChanged (object sender, MonoDevelop.Ide.Editor.LineEventArgs e)
{
- CommitDocumentUpdate ();
+ CommitMultipleLineUpdate (e.Line.LineNumber, e.Line.LineNumber);
}
void OnSyntaxModeChanged (SyntaxModeChangeEventArgs e)
@@ -213,6 +226,7 @@ namespace Mono.TextEditor this.TextBuffer.Properties.AddProperty(typeof(ITextDocument), this);
this.TextBuffer.Changed += this.OnTextBufferChanged;
+ (this.TextBuffer as Microsoft.VisualStudio.Text.Implementation.BaseBuffer).ChangedImmediate += OnTextBufferChangedImmediate;
this.TextBuffer.ContentTypeChanged += this.OnTextBufferContentTypeChanged;
this.VsTextDocument.FileActionOccurred += this.OnTextDocumentFileActionOccured;
@@ -230,7 +244,7 @@ namespace Mono.TextEditor SyntaxMode = null;
}
- void OnTextBufferChanged(object sender, Microsoft.VisualStudio.Text.TextContentChangedEventArgs args) + private void OnTextBufferChangedImmediate (object sender, Microsoft.VisualStudio.Text.TextContentChangedEventArgs args) { if (args.Changes == null) return;
@@ -239,14 +253,25 @@ namespace Mono.TextEditor changes.Add (new TextChange (change.OldPosition, change.NewPosition, change.OldText, change.NewText)); EnsureSegmentIsUnfolded(change.OldPosition, change.NewLength); }
- bool endUndo = false;
- UndoOperation operation = null;
var textChange = new TextChangeEventArgs(changes); InterruptFoldWorker(); TextChanging?.Invoke(this, textChange); // After TextChanging notification has been sent, we can update the cached snapshot
this.currentSnapshot = args.After; + }
+
+ void OnTextBufferChanged(object sender, Microsoft.VisualStudio.Text.TextContentChangedEventArgs args)
+ {
+ if (args.Changes == null)
+ return;
+ var changes = new List<TextChange> ();
+ foreach (var change in args.Changes) {
+ changes.Add (new TextChange (change.OldPosition, change.NewPosition, change.OldText, change.NewText));
+ }
+ bool endUndo = false;
+ UndoOperation operation = null;
+ var textChange = new TextChangeEventArgs(changes);
if (!isInUndo) {
operation = new UndoOperation(args);
@@ -294,11 +319,11 @@ namespace Mono.TextEditor public TextDocument (string fileName, string mimeType)
{
- var contentType = GetContentTypeFromMimeType (mimeType); + var contentType = (mimeType == null) ? PlatformCatalog.Instance.TextBufferFactoryService.InertContentType : GetContentTypeFromMimeType(fileName, mimeType);
Encoding enc;
var text = TextFileUtility.GetText (fileName, out enc);
var buffer = PlatformCatalog.Instance.TextBufferFactoryService.CreateTextBuffer (text ?? string.Empty, - PlatformCatalog.Instance.TextBufferFactoryService.InertContentType); + contentType); this.VsTextDocument = PlatformCatalog.Instance.TextDocumentFactoryService.CreateTextDocument (buffer, fileName); this.VsTextDocument.Encoding = enc; @@ -306,13 +331,14 @@ namespace Mono.TextEditor this.Initialize();
}
- public TextDocument (string text = null)
+ public TextDocument (string text = null, string fileName = null, string mimeType = null)
{
- var buffer = PlatformCatalog.Instance.TextBufferFactoryService.CreateTextBuffer(text ?? string.Empty,
- PlatformCatalog.Instance.TextBufferFactoryService.InertContentType);
+ var contentType = (mimeType == null) ? PlatformCatalog.Instance.TextBufferFactoryService.InertContentType : GetContentTypeFromMimeType(fileName, mimeType);
+ var buffer = PlatformCatalog.Instance.TextBufferFactoryService.CreateTextBuffer (text ?? string.Empty,
+ contentType);
- this.VsTextDocument = PlatformCatalog.Instance.TextDocumentFactoryService.CreateTextDocument(buffer, string.Empty);
- this.VsTextDocument.Encoding = MonoDevelop.Core.Text.TextFileUtility.DefaultEncoding;
+ this.VsTextDocument = PlatformCatalog.Instance.TextDocumentFactoryService.CreateTextDocument(buffer, fileName ?? string.Empty);
+ this.VsTextDocument.Encoding = TextFileUtility.DefaultEncoding;
this.Initialize();
}
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs index 8216ea1169..1609832a40 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs @@ -39,6 +39,7 @@ using System.Threading; using MonoDevelop.Ide; using System.Linq; using System.Threading.Tasks; +using MonoDevelop.Core; namespace Mono.TextEditor { @@ -205,12 +206,13 @@ namespace Mono.TextEditor { LineHeight = 16; - caret = new CaretImpl (this); - caret.PositionChanged += CaretPositionChanged; - options = TextEditorOptions.DefaultOptions; document = doc; AttachDocument (); + + caret = new CaretImpl (this); + caret.PositionChanged += CaretPositionChanged; + SearchEngine = new BasicSearchEngine (); HeightTree = new HeightTree (this); @@ -352,7 +354,7 @@ namespace Mono.TextEditor { if (str == null) throw new ArgumentNullException ("str"); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); foreach (char ch in str) { switch (ch) { case '&': @@ -376,7 +378,7 @@ namespace Mono.TextEditor break; } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } internal static int CalcIndentLength (string indent) @@ -424,7 +426,7 @@ namespace Mono.TextEditor int indentLength = -1; int curOffset = offset; - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); while (curOffset < offset + length && curOffset < Document.Length) { DocumentLine line = Document.GetLineByOffset (curOffset); int toOffset = System.Math.Min (line.Offset + line.Length, offset + length); @@ -481,7 +483,7 @@ namespace Mono.TextEditor if (result.Length > 0 && curOffset < offset + length) result.AppendLine (); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } internal async Task<IEnumerable<MonoDevelop.Ide.Editor.Highlighting.ColoredSegment>> GetChunks (DocumentLine line, int offset, int length) @@ -518,7 +520,7 @@ namespace Mono.TextEditor { if (string.IsNullOrEmpty (str)) return ""; - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); bool convertTabs = TabsToSpaces; var tabSize = Options.TabSize; for (int i = 0; i < str.Length; i++) { @@ -549,7 +551,7 @@ namespace Mono.TextEditor break; } } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } public string FormatString (int offset, string str) diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/UrlMarker.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/UrlMarker.cs index 07517710f7..79fb4fba8d 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/UrlMarker.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/UrlMarker.cs @@ -86,25 +86,9 @@ namespace Mono.TextEditor this.endColumn = endColumn; } - - void Doc_TextChanging (object sender, MonoDevelop.Core.Text.TextChangeEventArgs e) - { - var lineSegment = LineSegment.Segment; - for (int i = 0; i < e.TextChanges.Count; ++i) { - var change = e.TextChanges[i]; - if (lineSegment.IsInside (change.Offset) || lineSegment.IsInside (change.Offset + change.RemovalLength) || - change.Offset <= lineSegment.Offset && lineSegment.Offset <= change.Offset + change.RemovalLength) { - doc.RemoveMarker (this); - } - } - } - public void Dispose () { - if (doc != null) { - doc.TextChanging -= Doc_TextChanging; - doc = null; - } + doc = null; } public override void Draw (MonoTextEditor editor, Cairo.Context cr, LineMetrics metrics) @@ -146,9 +130,12 @@ namespace Mono.TextEditor to = System.Math.Max (to, editor.TextViewMargin.XOffset); if (@from < to) { if (color == null) { - foreach (var chunk in metrics.Layout.Chunks) - if (chunk.Contains (startOffset)) + foreach (var chunk in metrics.Layout.Chunks) { + if (chunk.Contains (markerStart)) { color = editor.EditorTheme.GetForeground (editor.EditorTheme.GetChunkStyle (chunk.ScopeStack)); + break; + } + } if (color == null) color = editor.EditorTheme.GetForeground (editor.EditorTheme.GetChunkStyle (new ScopeStack (style))); } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/PcFileCache.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/PcFileCache.cs index 167d48924e..3b7e7a3b88 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/PcFileCache.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/PcFileCache.cs @@ -29,6 +29,7 @@ using System.Text; using System.Xml; using System.IO; using System.Collections.Generic; +using MonoDevelop.Core; // IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT // This code is shared with xbuild, which has to build with .NET 2.0, @@ -535,7 +536,7 @@ namespace Mono.PkgConfig if (i == -1) return value; - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); int last = 0; while (i != -1 && i < value.Length) { sb.Append (value, last, i - last); @@ -546,6 +547,7 @@ namespace Mono.PkgConfig if (n == -1 || n == i) { // Closing bracket not found or empty name HasErrors = true; + StringBuilderCache.Free (sb); return value; } string rname = value.Substring (i, n - i); @@ -554,6 +556,7 @@ namespace Mono.PkgConfig sb.Append (rval); else { HasErrors = true; + StringBuilderCache.Free (sb); return value; } i = n + 1; @@ -565,7 +568,7 @@ namespace Mono.PkgConfig i = value.IndexOf ("${", i); } sb.Append (value, last, value.Length - last); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/LocalConsole.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/LocalConsole.cs index 17b496bf80..cadd1fd113 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/LocalConsole.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/LocalConsole.cs @@ -221,36 +221,36 @@ namespace MonoDevelop.Core.Execution public override string ReadLine () { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); while (LoadCurrent (true)) { for (int i=idx; i < current.Length; i++) { if (current[i] == '\n') { idx = i + 1; sb.Append (current, 0, i); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } if (current[i] == '\r') { idx = i + 1; sb.Append (current, 0, i); if (LoadCurrent (true) && current [idx] == '\n') idx++; - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } sb.Append (current, idx, current.Length - idx); current = null; } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } public override string ReadToEnd () { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); while (LoadCurrent (true)) { sb.Append (current, idx, current.Length - idx); current = null; } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessArgumentBuilder.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessArgumentBuilder.cs index f43d1d72a5..f875d79a2d 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessArgumentBuilder.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessArgumentBuilder.cs @@ -115,11 +115,11 @@ namespace MonoDevelop.Core.Execution /// arguments, only quoted arguments with escaped quotes.</remarks> public static string Quote (string s) { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); sb.Append ('"'); AppendEscaped (sb, escapeDoubleQuoteCharsStr, s); sb.Append ('"'); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } public override string ToString () diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Logging/AssertLoggingTraceListener.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Logging/AssertLoggingTraceListener.cs index 9cf02b44e3..3778df5720 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Logging/AssertLoggingTraceListener.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Logging/AssertLoggingTraceListener.cs @@ -45,7 +45,7 @@ namespace MonoDevelop.Core.Logging if (callerFrame == frames.Length - 1) callerFrame = 0; - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate(); if (IsRealMessage (message)) { if (!string.IsNullOrEmpty (detailMessage)) { sb.AppendFormat ("Failed assertion: {0} - {1}", message, detailMessage); @@ -61,7 +61,7 @@ namespace MonoDevelop.Core.Logging sb.Append ("\n"); FormatStackTrace (sb, frames, callerFrame); - LoggingService.LogError (sb.ToString ()); + LoggingService.LogError (StringBuilderCache.ReturnAndFree(sb)); } static bool IsRealMessage (string message) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextChangeEventArgs.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextChangeEventArgs.cs index 31e5958e46..2f312311ac 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextChangeEventArgs.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextChangeEventArgs.cs @@ -222,7 +222,7 @@ namespace MonoDevelop.Core.Text { if (text == null) return null; - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); foreach (var ch in text) { switch (ch) { case '\r': @@ -239,7 +239,7 @@ namespace MonoDevelop.Core.Text break; } } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj index 9fdd2d303a..122cc76e90 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj @@ -106,20 +106,17 @@ <Reference Include="Humanizer.Core"> <HintPath>..\..\..\packages\Humanizer.Core.2.2.0\lib\netstandard1.0\Humanizer.dll</HintPath> </Reference> - <Reference Include="Esent.Interop, Version=1.9.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> - <HintPath>..\..\..\packages\ManagedEsent.1.9.4\lib\net40\Esent.Interop.dll</HintPath> - </Reference> <Reference Include="SQLitePCLRaw.batteries_e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=17faffbb2a73a73f, processorArchitecture=MSIL"> - <HintPath>..\..\..\packages\SQLitePCLRaw.bundle_e_sqlite3.1.1.6\lib\net45\SQLitePCLRaw.batteries_e_sqlite3.dll</HintPath> + <HintPath>..\..\..\packages\SQLitePCLRaw.bundle_e_sqlite3.1.1.9\lib\net45\SQLitePCLRaw.batteries_e_sqlite3.dll</HintPath> </Reference> <Reference Include="SQLitePCLRaw.batteries_v2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL"> - <HintPath>..\..\..\packages\SQLitePCLRaw.bundle_e_sqlite3.1.1.6\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath> + <HintPath>..\..\..\packages\SQLitePCLRaw.bundle_e_sqlite3.1.1.9\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath> </Reference> <Reference Include="SQLitePCLRaw.core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL"> - <HintPath>..\..\..\packages\SQLitePCLRaw.core.1.1.6\lib\net45\SQLitePCLRaw.core.dll</HintPath> + <HintPath>..\..\..\packages\SQLitePCLRaw.core.1.1.9\lib\net45\SQLitePCLRaw.core.dll</HintPath> </Reference> <Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL"> - <HintPath>..\..\..\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.6\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath> + <HintPath>..\..\..\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.9\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath> </Reference> <Reference Include="Mono.Posix" /> <Reference Include="System.Composition.AttributedModel, Version=1.0.31.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> @@ -159,16 +156,16 @@ <Private>False</Private> </Reference> <Reference Include="Mono.Cecil"> - <HintPath>..\..\..\packages\Mono.Cecil.0.10.0-beta6\lib\net40\Mono.Cecil.dll</HintPath> + <HintPath>..\..\..\packages\Mono.Cecil.0.10.0-beta7\lib\net40\Mono.Cecil.dll</HintPath> </Reference> <Reference Include="Mono.Cecil.Mdb"> - <HintPath>..\..\..\packages\Mono.Cecil.0.10.0-beta6\lib\net40\Mono.Cecil.Mdb.dll</HintPath> + <HintPath>..\..\..\packages\Mono.Cecil.0.10.0-beta7\lib\net40\Mono.Cecil.Mdb.dll</HintPath> </Reference> <Reference Include="Mono.Cecil.Pdb"> - <HintPath>..\..\..\packages\Mono.Cecil.0.10.0-beta6\lib\net40\Mono.Cecil.Pdb.dll</HintPath> + <HintPath>..\..\..\packages\Mono.Cecil.0.10.0-beta7\lib\net40\Mono.Cecil.Pdb.dll</HintPath> </Reference> <Reference Include="Mono.Cecil.Rocks"> - <HintPath>..\..\..\packages\Mono.Cecil.0.10.0-beta6\lib\net40\Mono.Cecil.Rocks.dll</HintPath> + <HintPath>..\..\..\packages\Mono.Cecil.0.10.0-beta7\lib\net40\Mono.Cecil.Rocks.dll</HintPath> </Reference> <Reference Include="monodoc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" /> <Reference Include="mscorlib" /> @@ -241,6 +238,9 @@ <Reference Include="System.Xml.XPath.XDocument"> <HintPath>..\..\..\packages\System.Xml.XPath.XDocument.4.3.0\lib\net46\System.Xml.XPath.XDocument.dll</HintPath> </Reference> + <Reference Include="Microsoft.VisualStudio.CodingConventions"> + <HintPath>..\..\..\packages\Microsoft.VisualStudio.CodingConventions.1.1.20180226.4\lib\netstandard1.3\Microsoft.VisualStudio.CodingConventions.dll</HintPath> + </Reference> </ItemGroup> <ItemGroup> <Compile Include="MonoDevelop.Core\StringParserService.cs" /> @@ -756,6 +756,9 @@ <Compile Include="MonoDevelop.FSW\OSX\SR.cs" /> <Compile Include="MonoDevelop.FSW\FileSystemWatcher.cs" /> <Compile Include="MonoDevelop.FSW\Mono\FileSystemWatcher.cs" /> + <Compile Include="MonoDevelop.Core\StringBuilderCache.cs" /> + <Compile Include="MonoDevelop.Core\ObjectPool.cs" /> + <Compile Include="MonoDevelop.Core\SharedPools.cs" /> </ItemGroup> <ItemGroup> <None Include="Makefile.am" /> @@ -800,7 +803,7 @@ <InternalsVisibleTo Include="MonoDevelop.Ide.Tests" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> - <Target Name="BeforeBuild" Inputs="BuildVariables.cs.in; $(MSBuildProjectDirectory)\..\..\..\..\version.config" Outputs="BuildVariables.cs" Condition="Exists('$(MSBuildProjectDirectory)\..\..\..\..\version.config')" > + <Target Name="BeforeBuild" Inputs="BuildVariables.cs.in; $(MSBuildProjectDirectory)\..\..\..\..\version.config" Outputs="BuildVariables.cs" Condition="Exists('$(MSBuildProjectDirectory)\..\..\..\..\version.config')"> <MakeDir Directories="$(FullBuildInfo)" /> <Csc Sources="$(ConfigureScript)" OutputAssembly="$(ConfigureScriptExe)" ToolExe="$(CscToolExe)" ToolPath="$(CscToolPath)" Condition="!Exists('$(ConfigureScriptExe)')" /> <Exec Command="$(MonoLauncher)$(ConfigureScriptExe) gen-buildinfo $(FullBuildInfo)" WorkingDirectory="$(MSBuildProjectDirectory)" /> @@ -812,15 +815,16 @@ <Exec Command=""$(Git)" rev-parse HEAD > $(VcRevision)" WorkingDirectory="$(MSBuildProjectDirectory)" IgnoreExitCode="True" /> <RemoveDir Directories="$(FullBuildInfo)" /> </Target> - <Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.6\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.6\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" /> + <Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.9\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.9\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" /> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <PropertyGroup> <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> </PropertyGroup> - <Error Condition="!Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.6\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.6\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" /> - <Error Condition="!Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.6\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.6\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" /> - <Error Condition="!Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.6\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.6\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" /> + <Error Condition="!Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.9\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.9\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" /> + <Error Condition="!Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.9\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.9\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" /> + <Error Condition="!Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.9\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.9\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" /> </Target> - <Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.6\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.6\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" /> - <Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.6\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.6\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" /> + <Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.9\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.9\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" /> + <Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.9\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.9\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" /> + <Import Project="..\..\..\msbuild\SQLiteOSXHack.targets" /> </Project> diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ErrorHelper.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ErrorHelper.cs index 73e1f56ec5..610d7cd346 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ErrorHelper.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ErrorHelper.cs @@ -51,13 +51,13 @@ namespace MonoDevelop.Core var ae = (AggregateException)ex; if (ae.InnerExceptions.Count == 1) return GetErrorMessage (ae.InnerException); - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); foreach (var e in ae.InnerExceptions) { if (sb.Length > 0 && sb [sb.Length - 1] != '.') sb.Append (". "); sb.Append (GetErrorMessage (ex).Trim ()); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } else if (ex is UserException) { var ue = (UserException)ex; if (!string.IsNullOrEmpty (ue.Details)) { diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs index af14d65bad..f226217773 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs @@ -37,6 +37,7 @@ using MonoDevelop.Core.LogReporting; using MonoDevelop.Core.Logging; using Mono.Unix.Native; using System.Text; +using System.Collections.Immutable; namespace MonoDevelop.Core { @@ -46,7 +47,8 @@ namespace MonoDevelop.Core const string ReportCrashesKey = "MonoDevelop.LogAgent.ReportCrashes"; const string ReportUsageKey = "MonoDevelop.LogAgent.ReportUsage"; - static List<ILogger> loggers = new List<ILogger> (); + static object serviceLock = new object (); + static ImmutableList<ILogger> loggers = ImmutableList<ILogger>.Empty; static RemoteLogger remoteLogger; static DateTime timestamp; static int logFileSuffix; @@ -65,8 +67,8 @@ namespace MonoDevelop.Core static LoggingService () { var consoleLogger = new ConsoleLogger (); - loggers.Add (consoleLogger); - loggers.Add (new InstrumentationLogger ()); + loggers = loggers.Add (consoleLogger); + loggers = loggers.Add (new InstrumentationLogger ()); string consoleLogLevelEnv = Environment.GetEnvironmentVariable ("MONODEVELOP_CONSOLE_LOG_LEVEL"); if (!string.IsNullOrEmpty (consoleLogLevelEnv)) { @@ -88,7 +90,7 @@ namespace MonoDevelop.Core if (!string.IsNullOrEmpty (logFileEnv)) { try { var fileLogger = new FileLogger (logFileEnv); - loggers.Add (fileLogger); + loggers = loggers.Add (fileLogger); string logFileLevelEnv = Environment.GetEnvironmentVariable ("MONODEVELOP_FILE_LOG_LEVEL"); fileLogger.EnabledLevel = (EnabledLoggingLevel) Enum.Parse (typeof (EnabledLoggingLevel), logFileLevelEnv, true); } catch (Exception e) { @@ -412,17 +414,21 @@ namespace MonoDevelop.Core public static void AddLogger (ILogger logger) { - if (GetLogger (logger.Name) != null) - throw new Exception ("There is already a logger with the name '" + logger.Name + "'"); - loggers.Add (logger); + lock (serviceLock) { + if (GetLogger (logger.Name) != null) + throw new Exception ("There is already a logger with the name '" + logger.Name + "'"); + loggers = loggers.Add (logger); + } } public static void RemoveLogger (string name) { - ILogger logger = GetLogger (name); - if (logger == null) - throw new Exception ("There is no logger registered with the name '" + name + "'"); - loggers.Remove (logger); + lock (serviceLock) { + ILogger logger = GetLogger (name); + if (logger == null) + throw new Exception ("There is no logger registered with the name '" + name + "'"); + loggers = loggers.Remove (logger); + } } #endregion diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ObjectPool.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ObjectPool.cs new file mode 100644 index 0000000000..2d01f24c08 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ObjectPool.cs @@ -0,0 +1,269 @@ +ο»Ώ// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +// define TRACE_LEAKS to get additional diagnostics that can lead to the leak sources. note: it will +// make everything about 2-3x slower +// +// #define TRACE_LEAKS + +// define DETECT_LEAKS to detect possible leaks +// #if DEBUG +// #define DETECT_LEAKS //for now always enable DETECT_LEAKS in debug. +// #endif + +using System; +using System.Diagnostics; +using System.Threading; + +#if DETECT_LEAKS +using System.Runtime.CompilerServices; + +#endif + +namespace MonoDevelop.Core +{ + /// <summary> + /// Generic implementation of object pooling pattern with predefined pool size limit. The main + /// purpose is that limited number of frequently used objects can be kept in the pool for + /// further recycling. + /// + /// Notes: + /// 1) it is not the goal to keep all returned objects. Pool is not meant for storage. If there + /// is no space in the pool, extra returned objects will be dropped. + /// + /// 2) it is implied that if object was obtained from a pool, the caller will return it back in + /// a relatively short time. Keeping checked out objects for long durations is ok, but + /// reduces usefulness of pooling. Just new up your own. + /// + /// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice. + /// Rationale: + /// If there is no intent for reusing the object, do not use pool - just use "new". + /// </summary> + internal class ObjectPool<T> where T : class + { + [DebuggerDisplay ("{Value,nq}")] + private struct Element + { + internal T Value; + } + + /// <remarks> + /// Not using System.Func{T} because this file is linked into the (debugger) Formatter, + /// which does not have that type (since it compiles against .NET 2.0). + /// </remarks> + internal delegate T Factory (); + + // Storage for the pool objects. The first item is stored in a dedicated field because we + // expect to be able to satisfy most requests from it. + private T _firstItem; + private readonly Element [] _items; + + // factory is stored for the lifetime of the pool. We will call this only when pool needs to + // expand. compared to "new T()", Func gives more flexibility to implementers and faster + // than "new T()". + private readonly Factory _factory; + +#if DETECT_LEAKS + private static readonly ConditionalWeakTable<T, LeakTracker> leakTrackers = new ConditionalWeakTable<T, LeakTracker>(); + + private class LeakTracker : IDisposable + { + private volatile bool disposed; + +#if TRACE_LEAKS + internal volatile object Trace = null; +#endif + + public void Dispose() + { + disposed = true; + GC.SuppressFinalize(this); + } + + private string GetTrace() + { +#if TRACE_LEAKS + return Trace == null ? "" : Trace.ToString(); +#else + return "Leak tracing information is disabled. Define TRACE_LEAKS on ObjectPool`1.cs to get more info \n"; +#endif + } + + ~LeakTracker() + { + if (!this.disposed && !Environment.HasShutdownStarted) + { + var trace = GetTrace(); + + // If you are seeing this message it means that object has been allocated from the pool + // and has not been returned back. This is not critical, but turns pool into rather + // inefficient kind of "new". + Debug.WriteLine($"TRACEOBJECTPOOLLEAKS_BEGIN\nPool detected potential leaking of {typeof(T)}. \n Location of the leak: \n {GetTrace()} TRACEOBJECTPOOLLEAKS_END"); + } + } + } +#endif + + internal ObjectPool (Factory factory) + : this (factory, Environment.ProcessorCount * 2) + { } + + internal ObjectPool (Factory factory, int size) + { + Debug.Assert (size >= 1); + _factory = factory; + _items = new Element [size - 1]; + } + + private T CreateInstance () + { + var inst = _factory (); + return inst; + } + + /// <summary> + /// Produces an instance. + /// </summary> + /// <remarks> + /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. + /// Note that Free will try to store recycled objects close to the start thus statistically + /// reducing how far we will typically search. + /// </remarks> + internal T Allocate () + { + // PERF: Examine the first element. If that fails, AllocateSlow will look at the remaining elements. + // Note that the initial read is optimistically not synchronized. That is intentional. + // We will interlock only when we have a candidate. in a worst case we may miss some + // recently returned objects. Not a big deal. + T inst = _firstItem; + if (inst == null || inst != Interlocked.CompareExchange (ref _firstItem, null, inst)) { + inst = AllocateSlow (); + } + +#if DETECT_LEAKS + var tracker = new LeakTracker(); + leakTrackers.Add(inst, tracker); + +#if TRACE_LEAKS + var frame = CaptureStackTrace(); + tracker.Trace = frame; +#endif +#endif + return inst; + } + + private T AllocateSlow () + { + var items = _items; + + for (int i = 0; i < items.Length; i++) { + // Note that the initial read is optimistically not synchronized. That is intentional. + // We will interlock only when we have a candidate. in a worst case we may miss some + // recently returned objects. Not a big deal. + T inst = items [i].Value; + if (inst != null) { + if (inst == Interlocked.CompareExchange (ref items [i].Value, null, inst)) { + return inst; + } + } + } + + return CreateInstance (); + } + + /// <summary> + /// Returns objects to the pool. + /// </summary> + /// <remarks> + /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. + /// Note that Free will try to store recycled objects close to the start thus statistically + /// reducing how far we will typically search in Allocate. + /// </remarks> + internal void Free (T obj) + { + Validate (obj); + ForgetTrackedObject (obj); + + if (_firstItem == null) { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + _firstItem = obj; + } else { + FreeSlow (obj); + } + } + + private void FreeSlow (T obj) + { + var items = _items; + for (int i = 0; i < items.Length; i++) { + if (items [i].Value == null) { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + items [i].Value = obj; + break; + } + } + } + + /// <summary> + /// Removes an object from leak tracking. + /// + /// This is called when an object is returned to the pool. It may also be explicitly + /// called if an object allocated from the pool is intentionally not being returned + /// to the pool. This can be of use with pooled arrays if the consumer wants to + /// return a larger array to the pool than was originally allocated. + /// </summary> + [Conditional ("DEBUG")] + internal void ForgetTrackedObject (T old, T replacement = null) + { +#if DETECT_LEAKS + LeakTracker tracker; + if (leakTrackers.TryGetValue(old, out tracker)) + { + tracker.Dispose(); + leakTrackers.Remove(old); + } + else + { + var trace = CaptureStackTrace(); + Debug.WriteLine($"TRACEOBJECTPOOLLEAKS_BEGIN\nObject of type {typeof(T)} was freed, but was not from pool. \n Callstack: \n {trace} TRACEOBJECTPOOLLEAKS_END"); + } + + if (replacement != null) + { + tracker = new LeakTracker(); + leakTrackers.Add(replacement, tracker); + } +#endif + } + +#if DETECT_LEAKS + private static Lazy<Type> _stackTraceType = new Lazy<Type>(() => Type.GetType("System.Diagnostics.StackTrace")); + + private static object CaptureStackTrace() + { + return Activator.CreateInstance(_stackTraceType.Value); + } +#endif + + [Conditional ("DEBUG")] + private void Validate (object obj) + { + Debug.Assert (obj != null, "freeing null?"); + + Debug.Assert (_firstItem != obj, "freeing twice?"); + + var items = _items; + for (int i = 0; i < items.Length; i++) { + var value = items [i].Value; + if (value == null) { + return; + } + + Debug.Assert (value != obj, "freeing twice?"); + } + } + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Properties.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Properties.cs index 1f33234b3d..8b18107d45 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Properties.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Properties.cs @@ -344,7 +344,7 @@ namespace MonoDevelop.Core public override string ToString () { - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); result.Append ("[Properties:"); foreach (KeyValuePair<string, object> property in this.properties) { result.Append (property.Key); @@ -353,7 +353,7 @@ namespace MonoDevelop.Core result.Append (","); } result.Append ("]"); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public Properties Clone () diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/PropertyBag.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/PropertyBag.cs index 6f306b490d..5af30dff22 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/PropertyBag.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/PropertyBag.cs @@ -246,7 +246,7 @@ namespace MonoDevelop.Core string EscapeName (string str) { - StringBuilder sb = new StringBuilder (str.Length); + StringBuilder sb = StringBuilderCache.Allocate (); for (int n=0; n<str.Length; n++) { char c = str [n]; if (c == '_') @@ -259,24 +259,24 @@ namespace MonoDevelop.Core else sb.Append (c); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } string UnescapeName (string str) { - StringBuilder sb = new StringBuilder (str.Length); + StringBuilder sb = StringBuilderCache.Allocate (); for (int n=0; n<str.Length; n++) { char c = str [n]; if (c == '_') { if (n + 1 >= str.Length) - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); if (str [n + 1] == '_') { sb.Append (c); n++; } else { int len = int.Parse (str.Substring (n+1,1)); if (n + 2 + len - 1 >= str.Length) - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); int ic; if (int.TryParse (str.Substring (n + 2, len), NumberStyles.HexNumber, null, out ic)) sb.Append ((char)ic); @@ -285,7 +285,7 @@ namespace MonoDevelop.Core } else sb.Append (c); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs index a3d2c63e95..ac941637e6 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Runtime.cs @@ -221,7 +221,7 @@ namespace MonoDevelop.Core Counters.AddinsLoaded.Dec ("Add-in unloaded: " + args.AddinId); } - internal static bool Initialized { + public static bool Initialized { get { return initialized; } } @@ -417,6 +417,11 @@ namespace MonoDevelop.Core } /// <summary> + /// The main UI thread of the application. Needed to initialize the JoinableTaskContext. + /// </summary> + public static Thread MainThread => mainThread; + + /// <summary> /// Returns true if current thread is GUI thread. /// </summary> public static bool IsMainThread diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/SharedPools.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/SharedPools.cs new file mode 100644 index 0000000000..b74dd4a279 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/SharedPools.cs @@ -0,0 +1,58 @@ +ο»Ώ// +// StringBuilderCache.cs +// +// Author: +// Mike KrΓΌger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + + +namespace MonoDevelop.Core +{ + static class SharedPools + { + /// <summary> + /// pool that uses default constructor with 100 elements pooled + /// </summary> + public static ObjectPool<T> BigDefault<T> () where T : class, new() + { + return DefaultBigPool<T>.Instance; + } + + /// <summary> + /// pool that uses default constructor with 20 elements pooled + /// </summary> + public static ObjectPool<T> Default<T> () where T : class, new() + { + return DefaultNormalPool<T>.Instance; + } + + static class DefaultBigPool<T> where T : class, new() + { + public static readonly ObjectPool<T> Instance = new ObjectPool<T> (() => new T (), 100); + } + + static class DefaultNormalPool<T> where T : class, new() + { + public static readonly ObjectPool<T> Instance = new ObjectPool<T> (() => new T (), 20); + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringBuilderCache.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringBuilderCache.cs new file mode 100644 index 0000000000..b2957ed843 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringBuilderCache.cs @@ -0,0 +1,65 @@ +ο»Ώ// +// StringBuilderCache.cs +// +// Author: +// Mike KrΓΌger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Text; + +namespace MonoDevelop.Core +{ + /// <summary> + /// This is a pool for storing StringBuilder objects. + /// </summary> + public static class StringBuilderCache + { + const int Threshold = 4096; + + public static StringBuilder Allocate () + { + var result = SharedPools.Default<StringBuilder> ().Allocate (); + result.Clear (); + return result; + } + + public static StringBuilder Allocate (string text) + { + return Allocate ().Append (text); + } + + public static void Free (StringBuilder sb) + { + sb.Clear (); + if (sb.Capacity > Threshold) + sb.Capacity = Threshold; + SharedPools.Default<StringBuilder> ().Free (sb); + } + + public static string ReturnAndFree (StringBuilder sb) + { + var result = sb.ToString (); + Free (sb); + return result; + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringParserService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringParserService.cs index 77ebc71804..d78ff2f29d 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringParserService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringParserService.cs @@ -196,7 +196,7 @@ namespace MonoDevelop.Core public static string Parse (string input, IStringTagModel customTags) { - StringBuilder result = new StringBuilder (input.Length); + StringBuilder result = StringBuilderCache.Allocate (); int brace; int i = 0; @@ -225,7 +225,7 @@ namespace MonoDevelop.Core } i++; } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public static IEnumerable<IStringTagProvider> GetProviders () diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild.Conditions/ConditionParser.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild.Conditions/ConditionParser.cs index 65cc6cbfa4..98f58747ae 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild.Conditions/ConditionParser.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild.Conditions/ConditionParser.cs @@ -257,7 +257,7 @@ namespace MonoDevelop.Projects.MSBuild.Conditions { if (tokenizer.IsEOF ()) throw new ExpressionParseException ("Missing closing parenthesis in condition " + conditionStr); - StringBuilder sb = new StringBuilder (); + StringBuilder sb = Core.StringBuilderCache.Allocate (); sb.AppendFormat ("{0}({1}", prefix, tokenizer.Token.Value); tokenizer.GetNextToken (); @@ -280,7 +280,7 @@ namespace MonoDevelop.Projects.MSBuild.Conditions { sb.Append (")"); //FIXME: HACKY! - return new ConditionFactorExpression (new Token (sb.ToString (), TokenType.String, token_pos)); + return new ConditionFactorExpression (new Token (Core.StringBuilderCache.ReturnAndFree (sb), TokenType.String, token_pos)); } void ThrowParseException(TokenType type, string error_fmt, params object[] args) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/DefaultMSBuildEngine.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/DefaultMSBuildEngine.cs index e5e9a05e5c..90adb3c67b 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/DefaultMSBuildEngine.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/DefaultMSBuildEngine.cs @@ -64,6 +64,7 @@ namespace MonoDevelop.Projects.MSBuild public Dictionary<MSBuildImport, List<ProjectInfo>> ImportedProjects = new Dictionary<MSBuildImport, List<ProjectInfo>> (); public ConditionedPropertyCollection ConditionedProperties = new ConditionedPropertyCollection (); public List<GlobInfo> GlobIncludes = new List<GlobInfo> (); + public bool OnlyEvaluateProperties; public MSBuildProject GetRootMSBuildProject () { @@ -182,6 +183,11 @@ namespace MonoDevelop.Projects.MSBuild public override void Evaluate (object projectInstance) { + Evaluate (projectInstance, false); + } + + public override void Evaluate (object projectInstance, bool onlyEvaluateProperties) + { var pi = (ProjectInfo) projectInstance; pi.EvaluatedItemsIgnoringCondition.Clear (); @@ -190,6 +196,7 @@ namespace MonoDevelop.Projects.MSBuild pi.Imports.Clear (); pi.Targets.Clear (); pi.TargetsIgnoringCondition.Clear (); + pi.OnlyEvaluateProperties = onlyEvaluateProperties; // Unload referenced projects after evaluating to avoid unnecessary unload + load var oldRefProjects = pi.ReferencedProjects; @@ -248,13 +255,15 @@ namespace MonoDevelop.Projects.MSBuild LogEndEvalProject (context, pi); LogEndEvaluationStage (context); - LogBeginEvaluationStage (context, "Evaluating Items"); - LogBeginEvalProject (context, pi); + if (!pi.OnlyEvaluateProperties) { + LogBeginEvaluationStage (context, "Evaluating Items"); + LogBeginEvalProject (context, pi); - EvaluateObjects (pi, context, objects, true); + EvaluateObjects (pi, context, objects, true); - LogEndEvalProject (context, pi); - LogEndEvaluationStage (context); + LogEndEvalProject (context, pi); + LogEndEvaluationStage (context); + } // Once items have been evaluated, we need to re-evaluate properties that contain item transformations // (or that contain references to properties that have transformations). @@ -489,11 +498,14 @@ namespace MonoDevelop.Projects.MSBuild static void AddRemoveToGlobInclude (ProjectInfo project, MSBuildItem item, string remove) { var exclude = ExcludeToRegex (remove); - foreach (var globInclude in project.GlobIncludes.Where (g => g.Item.Name == item.Name)) { - if (globInclude.RemoveRegex != null) - exclude = globInclude.RemoveRegex + "|" + exclude; - globInclude.RemoveRegex = new Regex (exclude); - } + do { + foreach (var globInclude in project.GlobIncludes.Where (g => g.Item.Name == item.Name)) { + if (globInclude.RemoveRegex != null) + exclude = globInclude.RemoveRegex + "|" + exclude; + globInclude.RemoveRegex = new Regex (exclude); + } + project = project.Parent; + } while (project != null); } static void RemoveEvaluatedItemFromAllProjects (ProjectInfo project, MSBuildItem item, string include, bool trueCond) @@ -645,18 +657,18 @@ namespace MonoDevelop.Projects.MSBuild items = result; return true; } else if (ExecuteTransformItemListFunction (ref transformItems, itemFunction, itemFunctionArgs, out ignoreMetadata)) { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); for (int n = 0; n < transformItems.Length; n++) { if (n > 0) sb.Append (';'); sb.Append (transformItems[n].Include); } - items = sb.ToString (); + items = StringBuilderCache.ReturnAndFree (sb); return true; } } - var sbi = new StringBuilder (); + var sbi = StringBuilderCache.Allocate (); int count = 0; foreach (var eit in transformItems) { @@ -678,7 +690,7 @@ namespace MonoDevelop.Projects.MSBuild context.ClearItemContext (); } } - items = sbi.ToString (); + items = Core.StringBuilderCache.ReturnAndFree (sbi); return true; } @@ -943,7 +955,7 @@ namespace MonoDevelop.Projects.MSBuild static string ExcludeToRegex (string exclude, bool excludeDirectoriesOnly = false) { exclude = exclude.Replace ('/', '\\').Replace (@"\\", @"\"); - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); foreach (var ep in exclude.Split (new char [] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { var ex = ep.Trim (); if (excludeDirectoriesOnly) { @@ -977,7 +989,7 @@ namespace MonoDevelop.Projects.MSBuild } sb.Append ('$'); } - return sb.ToString (); + return Core.StringBuilderCache.ReturnAndFree (sb); } static char [] regexEscapeChars = { '\\', '^', '$', '{', '}', '[', ']', '(', ')', '.', '*', '+', '?', '|', '<', '>', '-', '&' }; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/EscapingUtilities.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/EscapingUtilities.cs index 94a180f36d..803ff6df8a 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/EscapingUtilities.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/EscapingUtilities.cs @@ -5,7 +5,8 @@ using System; using System.Collections.Generic;
using System.Globalization;
using System.Text;
-
+using MonoDevelop.Core; + namespace Microsoft.Build.Shared
{
/// <summary>
@@ -70,7 +71,7 @@ namespace Microsoft.Build.Shared }
// This is where we're going to build up the final string to return to the caller.
- StringBuilder unescapedString = new StringBuilder (escapedString.Length);
+ StringBuilder unescapedString = StringBuilderCache.Allocate ();
int currentPosition = 0;
@@ -112,7 +113,7 @@ namespace Microsoft.Build.Shared // characters into the destination.
unescapedString.Append(escapedString, currentPosition, escapedString.Length - currentPosition);
- return unescapedString.ToString ();
+ return StringBuilderCache.ReturnAndFree (unescapedString);
}
@@ -172,16 +173,16 @@ namespace Microsoft.Build.Shared }
// This is where we're going to build up the final string to return to the caller.
- StringBuilder escapedStringBuilder = new StringBuilder (unescapedString.Length * 2);
+ StringBuilder escapedStringBuilder = StringBuilderCache.Allocate ();
AppendEscapedString(escapedStringBuilder, unescapedString);
if (!cache)
{
- return escapedStringBuilder.ToString ();
+ return StringBuilderCache.ReturnAndFree (escapedStringBuilder);
}
- string escapedString = escapedStringBuilder.ToString ();
+ string escapedString = StringBuilderCache.ReturnAndFree (escapedStringBuilder);
lock (s_unescapedToEscapedStrings)
{
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/IMSBuildPropertySet.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/IMSBuildPropertySet.cs index 76e96caa7b..73990668a0 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/IMSBuildPropertySet.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/IMSBuildPropertySet.cs @@ -228,7 +228,7 @@ namespace MonoDevelop.Projects.MSBuild int i = val.IndexOfAny (new char[] {'\n','\r','\t'}); if (i != -1 || val [0] == '@') { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); if (i != -1) { int fi = val.IndexOf ('\\'); if (fi != -1 && fi < i) i = fi; @@ -248,7 +248,7 @@ namespace MonoDevelop.Projects.MSBuild else sb.Append (c); } - val = "@" + sb.ToString (); + val = "@" + StringBuilderCache.ReturnAndFree (sb); } char fc = val [0]; char lc = val [val.Length - 1]; @@ -283,7 +283,7 @@ namespace MonoDevelop.Projects.MSBuild end--; } if (val [start] == '@') { - StringBuilder sb = new StringBuilder (val.Length); + StringBuilder sb = StringBuilderCache.Allocate (); for (int n = start + 1; n < end; n++) { char c = val [n]; if (c == '\\') { @@ -294,7 +294,7 @@ namespace MonoDevelop.Projects.MSBuild } sb.Append (c); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } else return val.Substring (start, end - start); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEngine.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEngine.cs index 60cd785242..2e1ca86a37 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEngine.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEngine.cs @@ -68,6 +68,10 @@ namespace MonoDevelop.Projects.MSBuild { } + public virtual void Evaluate (object projectInstance, bool onlyEvaluateProperties) + { + } + public abstract bool GetItemHasMetadata (object item, string name); public abstract string GetItemMetadata (object item, string name); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEvaluationContext.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEvaluationContext.cs index 807075f2e0..7afbf74a5e 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEvaluationContext.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildEvaluationContext.cs @@ -841,9 +841,9 @@ namespace MonoDevelop.Projects.MSBuild if (sval != null && parameterType.IsEnum) { var enumValue = sval; - if (enumValue.StartsWith (parameterType.Name)) + if (enumValue.StartsWith (parameterType.Name, StringComparison.Ordinal)) enumValue = enumValue.Substring (parameterType.Name.Length + 1); - if (enumValue.StartsWith (parameterType.FullName)) + if (enumValue.StartsWith (parameterType.FullName, StringComparison.Ordinal)) enumValue = enumValue.Substring (parameterType.FullName.Length + 1); return Enum.Parse(parameterType, enumValue, ignoreCase: true); } @@ -854,9 +854,12 @@ namespace MonoDevelop.Projects.MSBuild var res = Convert.ChangeType (value, parameterType, CultureInfo.InvariantCulture); bool convertPath = false; - if ((method.DeclaringType == typeof (System.IO.File) || method.DeclaringType == typeof (System.IO.Directory)) && argNum == 0) { + if ((method.DeclaringType == typeof (System.IO.File) || method.DeclaringType == typeof (System.IO.Directory)) && argNum == 0) convertPath = true; - } else if (method.DeclaringType == typeof (IntrinsicFunctions)) { + else if (method.DeclaringType == typeof (System.IO.Path)) + // The windows path is already converted to a native path, but it may contain escape sequences + res = MSBuildProjectService.UnescapePath ((string)res); + else if (method.DeclaringType == typeof (IntrinsicFunctions)) { if (method.Name == "MakeRelative") convertPath = true; else if (method.Name == "GetDirectoryNameOfFileAbove" && argNum == 0) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildImport.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildImport.cs index d2efb3d97d..80dadba4be 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildImport.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildImport.cs @@ -68,12 +68,18 @@ namespace MonoDevelop.Projects.MSBuild public string Project { get { return target; } - set { AssertCanModify (); target = value; NotifyChanged (); } + set { AssertCanModify (); target = value; NotifyImportChanged (); } } public string Sdk { get { return sdk; } - set { AssertCanModify (); sdk = value; NotifyChanged (); } + set { AssertCanModify (); sdk = value; NotifyImportChanged (); } + } + + void NotifyImportChanged () + { + if (ParentProject != null) + ParentProject.NotifyImportChanged (); } internal override void Write (XmlWriter writer, WriteContext context) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProject.cs index 72c36e7b45..54bb12b055 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProject.cs @@ -432,6 +432,18 @@ namespace MonoDevelop.Projects.MSBuild changeStamp++; } + internal void NotifyImportChanged () + { + NotifyChanged (); + + ImportChanged?.Invoke (this, EventArgs.Empty); + } + + /// <summary> + /// Occurs when an import has changed, is added or removed. + /// </summary> + internal event EventHandler ImportChanged; + /// <summary> /// Gets or sets a value indicating whether this project uses the msbuild engine for evaluation. /// </summary> @@ -465,7 +477,7 @@ namespace MonoDevelop.Projects.MSBuild object readLock = new object (); - internal MSBuildProjectInstanceInfo LoadNativeInstance () + internal MSBuildProjectInstanceInfo LoadNativeInstance (bool evaluateItems) { lock (readLock) { var supportsMSBuild = UseMSBuildEngine && GetGlobalPropertyGroup ().GetValue ("UseMSBuildEngine", true); @@ -502,8 +514,10 @@ namespace MonoDevelop.Projects.MSBuild }; var xml = SaveToString (ctx); - foreach (var it in GetAllItems ()) - it.EvaluatedItemCount = 0; + if (evaluateItems) { + foreach (var it in GetAllItems ()) + it.EvaluatedItemCount = 0; + } nativeProjectInfo.Project = e.LoadProject (this, xml, FileName); } catch (Exception ex) { @@ -613,7 +627,7 @@ namespace MonoDevelop.Projects.MSBuild ChildNodes = ChildNodes.Add (import); import.ResetIndent (false); - NotifyChanged (); + NotifyImportChanged (); return import; } @@ -629,7 +643,7 @@ namespace MonoDevelop.Projects.MSBuild if (i != null) { i.RemoveIndent (); ChildNodes = ChildNodes.Remove (i); - NotifyChanged (); + NotifyImportChanged (); } } @@ -642,7 +656,7 @@ namespace MonoDevelop.Projects.MSBuild if (import.ParentObject == this) { import.RemoveIndent (); ChildNodes = ChildNodes.Remove (import); - NotifyChanged (); + NotifyImportChanged (); } else ((MSBuildImportGroup)import.ParentObject).RemoveImport (import); } @@ -1152,7 +1166,7 @@ namespace MonoDevelop.Projects.MSBuild if (elem == null) return ""; var node = elem.PreviousSibling; - StringBuilder res = new StringBuilder (); + StringBuilder res = StringBuilderCache.Allocate (); while (node != null) { var ws = node as XmlWhitespace; @@ -1163,13 +1177,13 @@ namespace MonoDevelop.Projects.MSBuild res.Append (t); } else { res.Append (t, i + 1, t.Length - i - 1); - return res.ToString (); + return StringBuilderCache.ReturnAndFree (res); } } else res.Clear (); node = node.PreviousSibling; } - return res.ToString (); + return StringBuilderCache.ReturnAndFree (res); } public static void Indent (TextFormatInfo format, XmlElement elem, bool closeInNewLine) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectInstance.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectInstance.cs index d470fbe8ff..73ddbe648a 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectInstance.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProjectInstance.cs @@ -87,7 +87,7 @@ namespace MonoDevelop.Projects.MSBuild if (projectInstance != null) engine.DisposeProjectInstance (projectInstance); - info = msproject.LoadNativeInstance (); + info = msproject.LoadNativeInstance (!OnlyEvaluateProperties); engine = info.Engine; projectInstance = engine.CreateProjectInstance (info.Project); @@ -103,7 +103,7 @@ namespace MonoDevelop.Projects.MSBuild foreach (var prop in globalProperties) engine.SetGlobalProperty (projectInstance, prop.Key, prop.Value); - engine.Evaluate (projectInstance); + engine.Evaluate (projectInstance, OnlyEvaluateProperties); SyncBuildProject (info.ItemMap, info.Engine, projectInstance); } catch (Exception ex) { diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs index ea8f0b4273..15dab944b7 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/RemoteBuildEngineManager.cs @@ -1,4 +1,4 @@ -ο»Ώ// +// // RemoteBuildEngineManager.cs // // Author: @@ -77,6 +77,7 @@ namespace MonoDevelop.Projects.MSBuild static bool searchPathConfigNeedsUpdate; static AsyncCriticalSection buildersLock = new AsyncCriticalSection (); static BuilderCache builders = new BuilderCache (); + static bool shutDown; internal static int EngineDisposalDelay = 60000; @@ -97,6 +98,27 @@ namespace MonoDevelop.Projects.MSBuild searchPathConfigNeedsUpdate = true; RecycleAllBuilders ().Ignore (); }; + Runtime.ShuttingDown += Shutdown; + } + + static async void Shutdown (object s, EventArgs a) + { + using (await buildersLock.EnterAsync ().ConfigureAwait (false)) { + shutDown = true; + foreach (var engine in builders.GetAllBuilders ().ToList ()) { + // Signal all project builder of the engine to stop + engine.Shutdown (); + engine.CancelScheduledDisposal (); + builders.Remove (engine); + engine.DisposeGracefully (); + } + } + } + + static void CheckShutDown () + { + if (shutDown) + throw new InvalidOperationException ("Runtime is shut down"); } internal static int ActiveEnginesCount => builders.GetAllBuilders ().Where (b => !b.IsShuttingDown).Count (); @@ -127,6 +149,8 @@ namespace MonoDevelop.Projects.MSBuild /// </remarks> public async static Task<IRemoteProjectBuilder> GetRemoteProjectBuilder (string projectFile, string solutionFile, TargetRuntime runtime, string minToolsVersion, bool requiresMicrosoftBuild, object buildSessionId, bool setBusy = false, bool allowBusy = true) { + CheckShutDown (); + RemoteBuildEngine engine; using (await buildersLock.EnterAsync ().ConfigureAwait (false)) { // Get a builder with the provided requirements @@ -270,6 +294,7 @@ namespace MonoDevelop.Projects.MSBuild { List<RemoteBuildEngine> projectBuilders; using (await buildersLock.EnterAsync ().ConfigureAwait (false)) { + if (shutDown) return; projectBuilders = builders.GetAllBuilders ().Where (b => b.IsProjectLoaded (projectFile)).ToList (); } foreach (var b in projectBuilders) @@ -282,6 +307,7 @@ namespace MonoDevelop.Projects.MSBuild public static async Task UnloadSolution (string solutionFile) { using (await buildersLock.EnterAsync ().ConfigureAwait (false)) { + if (shutDown) return; foreach (var engine in builders.GetAllBuilders ().Where (b => b.SolutionFile == solutionFile).ToList ()) ShutdownBuilderNoLock (engine); } @@ -294,6 +320,7 @@ namespace MonoDevelop.Projects.MSBuild { List<RemoteBuildEngine> projectBuilders; using (await buildersLock.EnterAsync ().ConfigureAwait (false)) { + if (shutDown) return; projectBuilders = builders.GetAllBuilders ().Where (b => b.IsProjectLoaded (projectFile)).ToList (); } foreach (var b in projectBuilders) @@ -307,6 +334,7 @@ namespace MonoDevelop.Projects.MSBuild { List<RemoteBuildEngine> projectBuilders; using (await buildersLock.EnterAsync ().ConfigureAwait (false)) { + if (shutDown) return; projectBuilders = builders.GetAllBuilders ().Where (b => b.IsProjectLoaded (projectFile)).ToList (); } foreach (var b in projectBuilders) @@ -325,6 +353,7 @@ namespace MonoDevelop.Projects.MSBuild public static async Task RecycleAllBuilders () { using (await buildersLock.EnterAsync ().ConfigureAwait (false)) { + if (shutDown) return; foreach (var b in builders.GetAllBuilders ().ToList ()) ShutdownBuilderNoLock (b); } @@ -338,6 +367,7 @@ namespace MonoDevelop.Projects.MSBuild /// <param name="verbosity">MSBuild verbosity.</param> internal static object StartBuildSession (TextWriter tw, MSBuildLogger logger, MSBuildVerbosity verbosity, ProjectConfigurationInfo[] configurations) { + CheckShutDown (); return new SessionInfo { Writer = tw, Verbosity = verbosity, @@ -354,6 +384,7 @@ namespace MonoDevelop.Projects.MSBuild internal static async Task EndBuildSession (object session) { using (await buildersLock.EnterAsync ().ConfigureAwait (false)) { + if (shutDown) return; foreach (var b in builders.GetAllBuilders ()) if (b.BuildSessionId == session) { b.BuildSessionId = null; @@ -367,6 +398,7 @@ namespace MonoDevelop.Projects.MSBuild // Update the global properties in all builders using (await buildersLock.EnterAsync ().ConfigureAwait (false)) { + if (shutDown) return; var gpp = (IMSBuildGlobalPropertyProvider)sender; foreach (var builder in builders.GetAllBuilders ()) await builder.SetGlobalProperties (new Dictionary<string, string> (gpp.GetGlobalProperties ())); @@ -609,12 +641,12 @@ namespace MonoDevelop.Projects.MSBuild spid = spid.Substring (0, i); int pid; if (int.TryParse (Path.GetFileName (spid), out pid)) { - try { - // If there is a process running with this id it means the builder is still being used - if (Process.GetProcessById (pid) != null) + try {
+ // If there is a process running with this id it means the builder is still being used
+ if (Process.GetProcessById (pid) != null)
continue; } catch { - // Ignore + // Ignore
} // No process for this id, it should be safe to delete the folder try { @@ -649,6 +681,8 @@ namespace MonoDevelop.Projects.MSBuild static Task ReleaseProjectBuilderNoLock (RemoteBuildEngine engine) { + if (shutDown) + return Task.CompletedTask; if (--engine.ReferenceCount != 0) return Task.CompletedTask; if (engine.IsShuttingDown) { @@ -674,6 +708,5 @@ namespace MonoDevelop.Projects.MSBuild } return Task.CompletedTask; } - } }
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs index 46768a95f0..6b0b36c660 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs @@ -115,7 +115,7 @@ namespace MonoDevelop.Projects.MSBuild resolvers.AddRange (assembly.ExportedTypes .Select (type => new { type, info = type.GetTypeInfo () }) - .Where (t => t.info.IsClass && t.info.IsPublic && typeof (SdkResolver).IsAssignableFrom (t.type)) + .Where (t => t.info.IsClass && t.info.IsPublic && !t.info.IsAbstract && typeof (SdkResolver).IsAssignableFrom (t.type)) .Select (t => (SdkResolver)Activator.CreateInstance (t.type))); } catch (Exception e) { logger.LogWarning (e.Message); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/PolicyService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/PolicyService.cs index 72e2c2df70..57e57f7ada 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/PolicyService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/PolicyService.cs @@ -530,7 +530,7 @@ namespace MonoDevelop.Projects.Policies } } - StringBuilder removed = new StringBuilder (); + StringBuilder removed = StringBuilderCache.Allocate (); for (int n=0; n<baseline.ItemData.Count; n++) { DataNode node = baseline.ItemData [n]; if (!extracted.Contains (node)) { @@ -545,6 +545,7 @@ namespace MonoDevelop.Projects.Policies if (removed.Length > 0) newItem.ItemData.Add (new DataValue ("__removed", removed.ToString ()) {StoreAsAttribute = true}); + StringBuilderCache.Free (removed); return newItem; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextFormatter.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextFormatter.cs index cc1f54ac19..bae1958b4b 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextFormatter.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextFormatter.cs @@ -26,6 +26,7 @@ using System; using System.Text; +using MonoDevelop.Core; namespace MonoDevelop.Projects.Text { @@ -343,17 +344,17 @@ namespace MonoDevelop.Projects.Text void CreateIndentString () { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); indentColumnWidth = AddIndentString (sb, indentString); - - paragFormattedIndentString = sb.ToString () + new string (' ', paragraphStartMargin); + sb.Append (new string (' ', paragraphStartMargin)); + paragFormattedIndentString = sb.ToString (); paragIndentColumnWidth = indentColumnWidth + paragraphStartMargin; if (LeftMargin > 0) { sb.Append (' ', LeftMargin); indentColumnWidth += LeftMargin; } - formattedIndentString = sb.ToString (); + formattedIndentString = StringBuilderCache.ReturnAndFree (sb); if (paragraphStart) curCol = paragIndentColumnWidth; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs index 9dee0e5bc1..07b2ca1735 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs @@ -1084,6 +1084,11 @@ namespace MonoDevelop.Projects if (result == null) return new List<PackageDependency> (); + if (monitor.CancellationToken.IsCancellationRequested && !result.Items.Any ()) { + // Avoid caching 0 items which can happen if a cancellation occurs. + return new List<PackageDependency> (); + } + packageDependencies = result.Items.Select (i => PackageDependency.Create (i)).Where (dependency => dependency != null).ToList (); packageDependenciesCache = packageDependenciesCache .SetItem (confId, packageDependencies); @@ -1571,17 +1576,17 @@ namespace MonoDevelop.Projects static string GetHierarchicalNamespace (string relativePath) { - StringBuilder sb = new StringBuilder (relativePath); + StringBuilder sb = StringBuilderCache.Allocate (relativePath); for (int i = 0; i < sb.Length; i++) { if (sb[i] == Path.DirectorySeparatorChar) sb[i] = '.'; } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } static string SanitisePotentialNamespace (string potential) { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); foreach (char c in potential) { if (char.IsLetter (c) || c == '_' || (sb.Length > 0 && (char.IsLetterOrDigit (sb[sb.Length - 1]) || sb[sb.Length - 1] == '_') && (c == '.' || char.IsNumber (c)))) { sb.Append (c); @@ -1591,9 +1596,10 @@ namespace MonoDevelop.Projects if (sb[sb.Length - 1] == '.') sb.Remove (sb.Length - 1, 1); - return sb.ToString (); - } else - return null; + return StringBuilderCache.ReturnAndFree (sb); + } + StringBuilderCache.Free (sb); + return null; } void RuntimeSystemAssemblyServiceDefaultRuntimeChanged (object sender, EventArgs e) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ItemCollection.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ItemCollection.cs index cad57828ac..c978c93e41 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ItemCollection.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ItemCollection.cs @@ -86,6 +86,17 @@ namespace MonoDevelop.Projects OnItemsRemoved (removedItems); } + internal void SetItems (IEnumerable<T> items, IEnumerable<T> newItems, IEnumerable<T> removedItems) + { + AssertCanWrite (); + + list = ImmutableList<T>.Empty.AddRange (items); + if (newItems.Any ()) + OnItemsAdded (newItems); + if (removedItems.Any ()) + OnItemsRemoved (removedItems); + } + IEnumerable<T> ReuseExistingItems (IEnumerable<T> items) { var updatedItems = new List<T> (); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MonoExecutionParameters.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MonoExecutionParameters.cs index b37e4343d6..f172182f44 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MonoExecutionParameters.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MonoExecutionParameters.cs @@ -269,7 +269,7 @@ namespace MonoDevelop.Projects public string GenerateDescription () { - StringBuilder ops = new StringBuilder (); + StringBuilder ops = StringBuilderCache.Allocate (); foreach (var kvp in itemPropertyAttributes) { var prop = kvp.Key; @@ -286,7 +286,7 @@ namespace MonoDevelop.Projects ops.Append (": ").Append (GetValue (pval)); } } - return ops.ToString (); + return StringBuilderCache.ReturnAndFree (ops); } public MonoExecutionParameters Clone () { diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs index 210b33befb..ff4e9112c5 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/Project.cs @@ -556,8 +556,29 @@ namespace MonoDevelop.Projects return results.ToArray (); } + /// <summary> + /// When the MSBuild imports in a project change we need to let the type system know so + /// it can update its source files. A NuGet package may contain only MSBuild targets + /// which modify the CoreCompileDependsOn property. Just having the MSBuild targets + /// file in the NuGet package will not trigger any notifications that the type system + /// is monitoring so here we trigger a Files notification. + /// </summary> + void OnMSBuildProjectImportChanged (object sender, EventArgs args) + { + lock (evaluatedCompileItemsLock) { + // Do not re-evaluate if the compile items have never been evaluated. + if (evaluatedCompileItemsTask != null) + reevaluateCoreCompileDependsOn = true; + } + + Runtime.RunInMainThread (() => { + NotifyModified ("Files"); + }).Ignore (); + } + object evaluatedCompileItemsLock = new object (); string evaluatedCompileItemsConfiguration; + bool reevaluateCoreCompileDependsOn; TaskCompletionSource<ProjectFile[]> evaluatedCompileItemsTask; /// <summary> @@ -573,17 +594,26 @@ namespace MonoDevelop.Projects TaskCompletionSource<ProjectFile []> currentTask = null; bool startTask = false; + bool reevaluate = false; lock (evaluatedCompileItemsLock) { - if (evaluatedCompileItemsConfiguration != config.Id) { + + if (evaluatedCompileItemsConfiguration != config.Id || reevaluateCoreCompileDependsOn) { // The configuration changed or query not yet done evaluatedCompileItemsConfiguration = config.Id; evaluatedCompileItemsTask = new TaskCompletionSource<ProjectFile []> (); startTask = true; + reevaluate = reevaluateCoreCompileDependsOn; + reevaluateCoreCompileDependsOn = false; } currentTask = evaluatedCompileItemsTask; } + if (reevaluate) { + // Ensure CoreCompileDependsOn is up to date. + await ReevaluateProject (monitor, resetCachedCompileItems: false); + } + if (startTask) { var coreCompileDependsOn = sourceProject.EvaluatedProperties.GetValue<string> ("CoreCompileDependsOn"); @@ -620,6 +650,7 @@ namespace MonoDevelop.Projects { lock (evaluatedCompileItemsLock) { evaluatedCompileItemsConfiguration = null; + reevaluateCoreCompileDependsOn = false; } } @@ -694,6 +725,9 @@ namespace MonoDevelop.Projects ProjectOpenedCounter.Inc (1, null, GetProjectEventMetadata (null)); + if (sourceProject != null) + sourceProject.ImportChanged += OnMSBuildProjectImportChanged; + InitializeFileWatcher (); } @@ -956,6 +990,12 @@ namespace MonoDevelop.Projects if (buildActions != null) return buildActions; + buildActions = GetBuildActions (predicate: null); + return buildActions; + } + + string[] GetBuildActions (Predicate<string> predicate) + { // find all the actions in use and add them to the list of standard actions HashSet<string> actions = new HashSet<string> (); //ad the standard actions @@ -972,6 +1012,9 @@ namespace MonoDevelop.Projects if (actions.Contains (action)) actions.Remove (action); + if (predicate != null) + commonActions = commonActions.Where (action => predicate (action)).ToList (); + //calculate dimensions for our new array and create it int dashPos = commonActions.Count; bool hasDash = commonActions.Count > 0 && actions.Count > 0; @@ -979,7 +1022,7 @@ namespace MonoDevelop.Projects int uncommonStart = hasDash ? dashPos + 1 : dashPos; if (hasDash) arrayLen++; - buildActions = new string[arrayLen]; + var buildActions = new string[arrayLen]; //populate it if (commonActions.Count > 0) @@ -999,6 +1042,18 @@ namespace MonoDevelop.Projects } return buildActions; } + + /// <summary> + /// Gets a list of build actions supported by this project for the file. + /// </summary> + /// <remarks> + /// Common actions are grouped at the top, separated by a "--" entry *IF* there are + /// "uncommon" actions and "common" actions + /// </remarks> + public string[] GetBuildActions (string fileName) + { + return GetBuildActions (buildAction => ProjectExtension.OnGetFileSupportsBuildAction (fileName, buildAction)); + } /// <summary> /// Gets a list of standard build actions. @@ -1016,6 +1071,11 @@ namespace MonoDevelop.Projects return BuildAction.StandardActions; } + protected virtual bool OnGetFileSupportsBuildAction (string fileName, string buildAction) + { + return true; + } + protected override void OnDispose () { DisposeFileWatcher (); @@ -1033,6 +1093,7 @@ namespace MonoDevelop.Projects RemoteBuildEngineManager.UnloadProject (FileName).Ignore (); if (sourceProject != null) { + sourceProject.ImportChanged -= OnMSBuildProjectImportChanged; sourceProject.Dispose (); sourceProject = null; } @@ -1584,6 +1645,12 @@ namespace MonoDevelop.Projects } } + ProjectFile newFile = CreateProjectFileForGlobItem (filename, buildAction); + if (newFile != null) { + Files.Add (newFile); + return newFile; + } + if (String.IsNullOrEmpty (buildAction)) { buildAction = GetDefaultBuildAction (filename); } @@ -1597,6 +1664,12 @@ namespace MonoDevelop.Projects { List<ProjectFile> newFiles = new List<ProjectFile> (); foreach (FilePath filename in files) { + ProjectFile newFile = CreateProjectFileForGlobItem (filename, buildAction); + if (newFile != null) { + newFiles.Add (newFile); + continue; + } + string ba = buildAction; if (String.IsNullOrEmpty (ba)) ba = GetDefaultBuildAction (filename); @@ -1607,6 +1680,31 @@ namespace MonoDevelop.Projects Files.AddRange (newFiles); return newFiles; } + + /// <summary> + /// Imported glob item may define a different build action and metadata for a file + /// so this is read and applied to the new ProjectFile. + /// </summary> + ProjectFile CreateProjectFileForGlobItem (FilePath file, string buildAction) + { + if (!UseAdvancedGlobSupport) + return null; + + var include = MSBuildProjectService.ToMSBuildPath (ItemDirectory, file); + var globItems = sourceProject.FindGlobItemsIncludingFile (include).ToList (); + if ((globItems.Count == 1) && (buildAction == null || globItems [0].Name == buildAction)) { + var eit = CreateFakeEvaluatedItem (sourceProject, globItems [0], include, null); + var projectFile = CreateProjectItem (eit) as ProjectFile; + if (projectFile != null) { + projectFile.Read (this, eit); + // Force UnevaluatedInclude to be reset to prevent Remove items + // being left in project after file is re-added. + projectFile.BackingItem = null; + return projectFile; + } + } + return null; + } /// <summary> /// Adds a file to the project @@ -2816,26 +2914,58 @@ namespace MonoDevelop.Projects if (loadedItems != null) loadedItems.Clear (); + HashSet<ProjectItem> unusedItems = null; + Dictionary<(string Name, string Include), ProjectItem> lookupItems = null; + ImmutableList<ProjectItem>.Builder newItems = null; + if (IsReevaluating) { + unusedItems = new HashSet<ProjectItem> (Items); + lookupItems = new Dictionary<(string Name, string Include), ProjectItem> (); + newItems = ImmutableList.CreateBuilder<ProjectItem> (); + + // Improve ReadItem performance by creating a dictionary of items that can be + // searched faster than using Items.FirstOrDefault. Building this dictionary takes ~15ms + foreach (var it in Items) { + if (it.BackingItem != null && it.BackingEvalItem != null) { + lookupItems.Add (GetProjectItemLookupKey (it.BackingEvalItem), it); + } + } + } + var localItems = new List<ProjectItem> (); foreach (var buildItem in msproject.EvaluatedItemsIgnoringCondition) { if (buildItem.IsImported && !ProjectExtension.OnGetSupportsImportedItem (buildItem)) continue; if (BuildAction.ReserverIdeActions.Contains (buildItem.Name)) continue; - ProjectItem it = ReadItem (buildItem); - if (it == null) + var result = ReadItem (buildItem, lookupItems); + if (result.Item == null) continue; - it.Flags = flags; - localItems.Add (it); - if (loadedItems != null) - loadedItems.Add (buildItem.SourceItem); + + result.Item.Flags = flags; + localItems.Add (result.Item); + if (result.IsNew) { + newItems?.Add (result.Item); + } else { + unusedItems?.Remove (result.Item); + } + + if (loadedItems != null) { + foreach (var item in buildItem.SourceItems) { + loadedItems.Add (item); + } + } } if (IsReevaluating) - Items.SetItems (localItems); + Items.SetItems (localItems, newItems, unusedItems); else Items.AddRange (localItems); } + static (string Name, string Include) GetProjectItemLookupKey (IMSBuildItemEvaluated item) + { + return (item.Name, item.Include); + } + protected override void OnSetFormat (MSBuildFileFormat format) { base.OnSetFormat (format); @@ -2853,15 +2983,16 @@ namespace MonoDevelop.Projects productVersion = FileFormat.DefaultProductVersion; } - internal ProjectItem ReadItem (IMSBuildItemEvaluated buildItem) + internal (ProjectItem Item, bool IsNew) ReadItem (IMSBuildItemEvaluated buildItem, Dictionary<(string Name, string Include), ProjectItem> lookupItems) { if (IsReevaluating) { // If this item already exists in the current collection of items, reuse it - var eit = Items.FirstOrDefault (it => it.BackingItem != null && it.BackingEvalItem != null && it.BackingEvalItem.Name == buildItem.Name && it.BackingEvalItem.Include == buildItem.Include && ItemsAreEqual (buildItem, it.BackingEvalItem)); - if (eit != null) { - eit.BackingItem = buildItem.SourceItem; - eit.BackingEvalItem = buildItem; - return eit; + if (lookupItems.TryGetValue (GetProjectItemLookupKey (buildItem), out ProjectItem eit)) { + if (ItemsAreEqual (buildItem, eit.BackingEvalItem) || CheckProjectReferenceItemsAreEqual (buildItem, eit)) { + eit.BackingItem = buildItem.SourceItem; + eit.BackingEvalItem = buildItem; + return (eit, false); + } } } @@ -2869,7 +3000,27 @@ namespace MonoDevelop.Projects item.Read (this, buildItem); item.BackingItem = buildItem.SourceItem; item.BackingEvalItem = buildItem; - return item; + return (item, true); + } + + /// <summary> + /// Special case ProjectReference items when checking for a match for ReadItem. The underlying build + /// items may not have matching metadata properties but the ProjectReference.Equals method may + /// indicate a match. This is tested for in the ProjectReevaluationTests + /// ReevaluateNewProjectReferencesAfterSave test. + /// </summary> + bool CheckProjectReferenceItemsAreEqual (IMSBuildItemEvaluated buildItem, ProjectItem item) + { + if (!(item is ProjectReference existingProjectReference)) + return false; + + var newProjectReference = CreateProjectItem (buildItem) as ProjectReference; + if (newProjectReference != null) { + newProjectReference.Read (this, buildItem); + return item.Equals (newProjectReference); + } + + return false; } struct MergedPropertyValue @@ -3275,6 +3426,14 @@ namespace MonoDevelop.Projects /// </summary> public bool UseAdvancedGlobSupport { get; set; } + /// <summary> + /// When set to true if new file is added to a project that does not have + /// the metadata properties defined by a update glob item then the item will + /// not be excluded but will be treated as though it had these metadata properties + /// with the same values. + /// </summary> + public bool UseDefaultMetadataForExcludedExpandedItems { get; set; } + HashSet<MSBuildItem> usedMSBuildItems = new HashSet<MSBuildItem> (); HashSet<ProjectItem> loadedProjectItems = new HashSet<ProjectItem> (); @@ -3333,7 +3492,7 @@ namespace MonoDevelop.Projects foreach (var it in unusedItems) { if (it.ParentGroup != null) { // It may already have been deleted // Remove wildcard item if it is not imported. - if (!it.IsWildcardItem || it.ParentProject == msproject) { + if ((!it.IsWildcardItem && it.ParentProject == msproject) || it.ParentProject == msproject) { msproject.RemoveItem (it); if (!UseAdvancedGlobSupport) @@ -3496,6 +3655,13 @@ namespace MonoDevelop.Projects loadedItems.Add (buildItem); unusedItems.Remove (buildItem); + if (sourceItems != null) { + foreach (var sourceItem in sourceItems) { + loadedItems.Add (sourceItem); + unusedItems.Remove (sourceItem); + } + } + if (!buildItem.IsWildcardItem) { if (buildItem.IsUpdate) { var propertiesAlreadySet = new HashSet<string> (buildItem.Metadata.GetProperties ().Select (p => p.Name)); @@ -3597,6 +3763,8 @@ namespace MonoDevelop.Projects updateItems = FindUpdateItemsForItem (globItem, item.Include).ToList (); updateItem = updateItems.LastOrDefault (); if (updateItem == null) { + if (UpdateGlobHasMatchingPropertyValue (p, evalItem)) + continue; // There is no existing update item. A new one will be generated. generateNewUpdateItem = true; continue; @@ -3661,7 +3829,7 @@ namespace MonoDevelop.Projects it.ParentProject.RemoveItem (it); } // If this metadata is defined in the glob item, the only option is to exclude the item from the glob. - if (globItem.Metadata.HasProperty (p.Name)) { + if (globItem.Metadata.HasProperty (p.Name) && !UseDefaultMetadataForExcludedExpandedItems) { // Get rid of all update items, not needed anymore since a full new item will be added foreach (var it in updateItems) { if (it.ParentNode != null) @@ -3702,6 +3870,28 @@ namespace MonoDevelop.Projects } } + bool UpdateGlobHasMatchingPropertyValue (MSBuildProperty p, IMSBuildItemEvaluated evalItem) + { + MSBuildEvaluationContext context = null; + + foreach (var updateItem in evalItem.SourceItems) { + if (!updateItem.IsUpdate) + continue; + + var p2 = updateItem.Metadata.GetProperty (p.Name); + if (p2 != null) { + if (context == null) { + context = new MSBuildEvaluationContext (); + context.InitEvaluation (MSBuildProject); + } + + string value = context.Evaluate (p.UnevaluatedValue); + return p.ValueType.Equals (p.Value, value); + } + } + return false; + } + bool ItemsAreEqual (IMSBuildItemEvaluated item1, IMSBuildItemEvaluated item2) { // Compare only metadata, since item name and include can't change @@ -3811,6 +4001,15 @@ namespace MonoDevelop.Projects /// </remarks> public Task ReevaluateProject (ProgressMonitor monitor) { + return ReevaluateProject (monitor, true); + } + + /// <summary> + /// Reevaluates the MSBuild project and optionally resets the cached compile items + /// taken from CoreCompileDependsOn. + /// </summary> + Task ReevaluateProject (ProgressMonitor monitor, bool resetCachedCompileItems) + { return BindTask (ct => Runtime.RunInMainThread (async () => { using (await writeProjectLock.EnterAsync ()) { var oldCapabilities = new HashSet<string> (projectCapabilities); @@ -3834,7 +4033,8 @@ namespace MonoDevelop.Projects IsReevaluating = false; } - ResetCachedCompileItems (); + if (resetCachedCompileItems) + ResetCachedCompileItems (); if (!oldCapabilities.SetEquals (projectCapabilities)) NotifyProjectCapabilitiesChanged (); @@ -4038,7 +4238,14 @@ namespace MonoDevelop.Projects } string include = MSBuildProjectService.ToMSBuildPath (ItemDirectory, fileName); - foreach (var it in sourceProject.FindGlobItemsIncludingFile (include).Where (it => it.Metadata.GetProperties ().Count () == 0)) { + var globItems = sourceProject.FindGlobItemsIncludingFile (include); + if (globItems == null) { + // If the MSBuildEngine no glob items can be found. + LoggingService.LogWarning ("File created externally not processed. {0}", fileName); + return; + } + + foreach (var it in globItems.Where (it => it.Metadata.GetProperties ().Count () == 0)) { var eit = CreateFakeEvaluatedItem (sourceProject, it, include, null); var pi = CreateProjectItem (eit); pi.Read (this, eit); @@ -4235,6 +4442,11 @@ namespace MonoDevelop.Projects return Project.OnGetCommonBuildActions (); } + internal protected override bool OnGetFileSupportsBuildAction (string fileName, string buildAction) + { + return Project.OnGetFileSupportsBuildAction (fileName, buildAction); + } + internal protected override ProjectItem OnCreateProjectItem (IMSBuildItemEvaluated item) { return Project.OnCreateProjectItem (item); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectExtension.cs index 3544528240..b99a83ead4 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectExtension.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectExtension.cs @@ -205,6 +205,11 @@ namespace MonoDevelop.Projects return next.OnGetCommonBuildActions (); } + internal protected virtual bool OnGetFileSupportsBuildAction (string fileName, string buildAction) + { + return next.OnGetFileSupportsBuildAction (fileName, buildAction); + } + internal protected virtual ProjectItem OnCreateProjectItem (IMSBuildItemEvaluated item) { return next.OnCreateProjectItem (item); diff --git a/main/src/core/MonoDevelop.Core/packages.config b/main/src/core/MonoDevelop.Core/packages.config index b71392f474..6600de18a0 100644 --- a/main/src/core/MonoDevelop.Core/packages.config +++ b/main/src/core/MonoDevelop.Core/packages.config @@ -1,90 +1,110 @@ -<?xml version="1.0" encoding="utf-8"?> -<packages> - <package id="Humanizer.Core" version="2.2.0" targetFramework="net461" /> - <package id="ManagedEsent" version="1.9.4" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis" version="2.7.0-beta3-62509-03" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.Analyzers" version="1.2.0-beta2" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.Common" version="2.7.0-beta3-62509-03" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.CSharp" version="2.7.0-beta3-62509-03" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.CSharp.Features" version="2.7.0-beta3-62509-03" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="2.7.0-beta3-62509-03" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.EditorFeatures" version="2.7.0-beta3-62509-03" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.EditorFeatures.Text" version="2.7.0-beta3-62509-03" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.Elfie" version="1.0.0-rc9" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.Features" version="2.7.0-beta3-62509-03" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.VisualBasic" version="2.7.0-beta3-62509-03" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.VisualBasic.Features" version="2.7.0-beta3-62509-03" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.VisualBasic.Workspaces" version="2.7.0-beta3-62509-03" targetFramework="net461" /> - <package id="Microsoft.CodeAnalysis.Workspaces.Common" version="2.7.0-beta3-62509-03" targetFramework="net461" /> - <package id="Microsoft.Composition" version="1.0.30" targetFramework="net461" /> - <package id="Microsoft.VisualStudio.Composition" version="15.3.38" targetFramework="net461" /> - <package id="Microsoft.VisualStudio.CoreUtility" version="15.6.161-preview" targetFramework="net45" /> - <package id="Microsoft.VisualStudio.Language.Intellisense" version="15.6.161-preview" targetFramework="net45" /> - <package id="Microsoft.VisualStudio.Language.StandardClassification" version="15.6.161-preview" targetFramework="net45" /> - <package id="Microsoft.VisualStudio.Text.Data" version="15.6.161-preview" targetFramework="net45" /> - <package id="Microsoft.VisualStudio.Text.Internal" version="15.6.161-preview" targetFramework="net45" /> - <package id="Microsoft.VisualStudio.Text.Logic" version="15.6.161-preview" targetFramework="net45" /> - <package id="Microsoft.VisualStudio.Text.UI" version="15.6.161-preview" targetFramework="net45" /> - <package id="Microsoft.VisualStudio.Text.UI.Wpf" version="15.6.161-preview" targetFramework="net45" /> - <package id="Microsoft.VisualStudio.Text.Implementation" version="15.0.7-pre" targetFramework="net45" /> - <package id="Microsoft.VisualStudio.Threading" version="15.4.4" targetFramework="net461" /> - <package id="Microsoft.VisualStudio.Validation" version="15.3.15" targetFramework="net461" /> - <package id="Mono.Cecil" version="0.10.0-beta6" targetFramework="net45" /> - <package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" /> - <package id="SharpZipLib" version="0.86.0" targetFramework="net45" /> - <package id="SQLitePCLRaw.bundle_e_sqlite3" version="1.1.6" targetFramework="net461" /> - <package id="SQLitePCLRaw.core" version="1.1.6" targetFramework="net461" /> - <package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.6" targetFramework="net461" /> - <package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.6" targetFramework="net461" /> - <package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.6" targetFramework="net461" /> - <package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.6" targetFramework="net461" /> - <package id="System.AppContext" version="4.3.0" targetFramework="net461" /> - <package id="System.Collections" version="4.3.0" targetFramework="net461" /> - <package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net461" /> - <package id="System.Collections.Immutable" version="1.3.1" targetFramework="net461" /> - <package id="System.Composition.AttributedModel" version="1.0.31" targetFramework="net461" /> - <package id="System.Composition.Hosting" version="1.0.31" targetFramework="net461" /> - <package id="System.Composition.Runtime" version="1.0.31" targetFramework="net461" /> - <package id="System.Composition.TypedParts" version="1.0.31" targetFramework="net461" /> - <package id="System.Console" version="4.3.0" targetFramework="net461" /> - <package id="System.Diagnostics.Contracts" version="4.3.0" targetFramework="net461" /> - <package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net461" /> - <package id="System.Diagnostics.FileVersionInfo" version="4.3.0" targetFramework="net461" /> - <package id="System.Diagnostics.StackTrace" version="4.3.0" targetFramework="net461" /> - <package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net461" /> - <package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net461" /> - <package id="System.Globalization" version="4.3.0" targetFramework="net461" /> - <package id="System.IO.Compression" version="4.3.0" targetFramework="net461" /> - <package id="System.IO.FileSystem" version="4.3.0" targetFramework="net461" /> - <package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net461" /> - <package id="System.Linq" version="4.3.0" targetFramework="net461" /> - <package id="System.Linq.Expressions" version="4.3.0" targetFramework="net461" /> - <package id="System.Linq.Parallel" version="4.3.0" targetFramework="net461" /> - <package id="System.ObjectModel" version="4.3.0" targetFramework="net461" /> - <package id="System.Reflection" version="4.3.0" targetFramework="net461" /> - <package id="System.Reflection.Metadata" version="1.4.2" targetFramework="net461" /> - <package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net461" /> - <package id="System.Runtime" version="4.3.0" targetFramework="net461" /> - <package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net461" /> - <package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net461" /> - <package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net461" /> - <package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net461" /> - <package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net461" /> - <package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net461" /> - <package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" /> - <package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net461" /> - <package id="System.Text.Encoding" version="4.3.0" targetFramework="net461" /> - <package id="System.Text.Encoding.CodePages" version="4.3.0" targetFramework="net461" /> - <package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net461" /> - <package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net461" /> - <package id="System.Threading" version="4.3.0" targetFramework="net461" /> - <package id="System.Threading.Tasks" version="4.3.0" targetFramework="net461" /> - <package id="System.Threading.Tasks.Parallel" version="4.3.0" targetFramework="net461" /> - <package id="System.Threading.Thread" version="4.3.0" targetFramework="net461" /> - <package id="System.ValueTuple" version="4.4.0" targetFramework="net461" /> - <package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net461" /> - <package id="System.Xml.XDocument" version="4.3.0" targetFramework="net461" /> - <package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="net461" /> - <package id="System.Xml.XPath" version="4.3.0" targetFramework="net461" /> - <package id="System.Xml.XPath.XDocument" version="4.3.0" targetFramework="net461" /> -</packages> +ο»Ώ<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="Humanizer.Core" version="2.2.0" targetFramework="net461" />
+ <package id="ManagedEsent" version="1.9.4" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis" version="2.8.0-beta2-62708-11" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.Analyzers" version="1.2.0-beta2" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.Common" version="2.8.0-beta2-62708-11" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.CSharp" version="2.8.0-beta2-62708-11" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.CSharp.Features" version="2.8.0-beta2-62708-11" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.CSharp.Workspaces" version="2.8.0-beta2-62708-11" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.EditorFeatures" version="2.8.0-beta2-62708-11" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.EditorFeatures.Text" version="2.8.0-beta2-62708-11" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.Elfie" version="1.0.0-rc9" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.Features" version="2.8.0-beta2-62708-11" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.VisualBasic" version="2.8.0-beta2-62708-11" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.VisualBasic.Features" version="2.8.0-beta2-62708-11" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.VisualBasic.Workspaces" version="2.8.0-beta2-62708-11" targetFramework="net461" />
+ <package id="Microsoft.CodeAnalysis.Workspaces.Common" version="2.8.0-beta2-62708-11" targetFramework="net461" />
+ <package id="Microsoft.Composition" version="1.0.30" targetFramework="net461" />
+ <package id="Microsoft.NETCore.Platforms" version="1.0.1" targetFramework="net461" />
+ <package id="Microsoft.NETCore.Portable.Compatibility" version="1.0.1" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.CodingConventions" version="1.1.20180226.4" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.Composition" version="15.6.36" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.CoreUtility" version="15.6.281-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Language" version="15.6.281-preview" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.Language.Intellisense" version="15.6.281-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Language.StandardClassification" version="15.6.281-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Text.Data" version="15.6.281-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Text.Internal" version="15.6.281-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Text.Logic" version="15.6.281-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Text.UI" version="15.6.281-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Text.UI.Wpf" version="15.6.281-preview" targetFramework="net45" />
+ <package id="Microsoft.VisualStudio.Text.Implementation" version="15.2.0-pre" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.Threading" version="15.6.46" targetFramework="net461" />
+ <package id="Microsoft.VisualStudio.Validation" version="15.3.32" targetFramework="net461" />
+ <package id="Microsoft.Win32.Primitives" version="4.0.1" targetFramework="net461" />
+ <package id="Mono.Cecil" version="0.10.0-beta7" targetFramework="net45" />
+ <package id="NETStandard.Library" version="1.6.0" targetFramework="net461" />
+ <package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
+ <package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
+ <package id="SQLitePCLRaw.bundle_e_sqlite3" version="1.1.9" targetFramework="net461" />
+ <package id="SQLitePCLRaw.core" version="1.1.9" targetFramework="net461" />
+ <package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.9" targetFramework="net461" />
+ <package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.9" targetFramework="net461" />
+ <package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.9" targetFramework="net461" />
+ <package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.9" targetFramework="net461" />
+ <package id="System.AppContext" version="4.3.0" targetFramework="net461" />
+ <package id="System.Collections" version="4.3.0" targetFramework="net461" />
+ <package id="System.Collections.Concurrent" version="4.3.0" targetFramework="net461" />
+ <package id="System.Collections.Immutable" version="1.3.1" targetFramework="net461" />
+ <package id="System.Composition" version="1.0.31" targetFramework="net461" />
+ <package id="System.Composition.AttributedModel" version="1.0.31" targetFramework="net461" />
+ <package id="System.Composition.Convention" version="1.0.31" targetFramework="net461" />
+ <package id="System.Composition.Hosting" version="1.0.31" targetFramework="net461" />
+ <package id="System.Composition.Runtime" version="1.0.31" targetFramework="net461" />
+ <package id="System.Composition.TypedParts" version="1.0.31" targetFramework="net461" />
+ <package id="System.Console" version="4.3.0" targetFramework="net461" />
+ <package id="System.Diagnostics.Contracts" version="4.3.0" targetFramework="net461" />
+ <package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net461" />
+ <package id="System.Diagnostics.DiagnosticSource" version="4.0.0" targetFramework="net461" />
+ <package id="System.Diagnostics.FileVersionInfo" version="4.3.0" targetFramework="net461" />
+ <package id="System.Diagnostics.StackTrace" version="4.3.0" targetFramework="net461" />
+ <package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net461" />
+ <package id="System.Diagnostics.Tracing" version="4.1.0" targetFramework="net461" />
+ <package id="System.Dynamic.Runtime" version="4.3.0" targetFramework="net461" />
+ <package id="System.Globalization" version="4.3.0" targetFramework="net461" />
+ <package id="System.Globalization.Calendars" version="4.0.1" targetFramework="net461" />
+ <package id="System.IO" version="4.1.0" targetFramework="net461" />
+ <package id="System.IO.Compression" version="4.3.0" targetFramework="net461" />
+ <package id="System.IO.Compression.ZipFile" version="4.0.1" targetFramework="net461" />
+ <package id="System.IO.FileSystem" version="4.3.0" targetFramework="net461" />
+ <package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net461" />
+ <package id="System.Linq" version="4.3.0" targetFramework="net461" />
+ <package id="System.Linq.Expressions" version="4.3.0" targetFramework="net461" />
+ <package id="System.Linq.Parallel" version="4.3.0" targetFramework="net461" />
+ <package id="System.Net.Http" version="4.1.0" targetFramework="net461" />
+ <package id="System.Net.Primitives" version="4.0.11" targetFramework="net461" />
+ <package id="System.Net.Sockets" version="4.1.0" targetFramework="net461" />
+ <package id="System.ObjectModel" version="4.3.0" targetFramework="net461" />
+ <package id="System.Reflection" version="4.3.0" targetFramework="net461" />
+ <package id="System.Reflection.Extensions" version="4.0.1" targetFramework="net461" />
+ <package id="System.Reflection.Metadata" version="1.4.2" targetFramework="net461" />
+ <package id="System.Reflection.Primitives" version="4.0.1" targetFramework="net461" />
+ <package id="System.Resources.ResourceManager" version="4.3.0" targetFramework="net461" />
+ <package id="System.Runtime" version="4.3.0" targetFramework="net461" />
+ <package id="System.Runtime.Extensions" version="4.3.0" targetFramework="net461" />
+ <package id="System.Runtime.Handles" version="4.0.1" targetFramework="net461" />
+ <package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net461" />
+ <package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net461" />
+ <package id="System.Runtime.Numerics" version="4.3.0" targetFramework="net461" />
+ <package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net461" />
+ <package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net461" />
+ <package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" />
+ <package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net461" />
+ <package id="System.Text.Encoding" version="4.3.0" targetFramework="net461" />
+ <package id="System.Text.Encoding.CodePages" version="4.3.0" targetFramework="net461" />
+ <package id="System.Text.Encoding.Extensions" version="4.3.0" targetFramework="net461" />
+ <package id="System.Text.RegularExpressions" version="4.3.0" targetFramework="net461" />
+ <package id="System.Threading" version="4.3.0" targetFramework="net461" />
+ <package id="System.Threading.Tasks" version="4.3.0" targetFramework="net461" />
+ <package id="System.Threading.Tasks.Parallel" version="4.3.0" targetFramework="net461" />
+ <package id="System.Threading.Thread" version="4.3.0" targetFramework="net461" />
+ <package id="System.Threading.Timer" version="4.0.1" targetFramework="net461" />
+ <package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
+ <package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net461" />
+ <package id="System.Xml.XDocument" version="4.3.0" targetFramework="net461" />
+ <package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="net461" />
+ <package id="System.Xml.XPath" version="4.3.0" targetFramework="net461" />
+ <package id="System.Xml.XPath.XDocument" version="4.3.0" targetFramework="net461" />
+</packages>
diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml index 4423f6f512..3846ff564c 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/MonoDevelop.Ide.addin.xml @@ -238,6 +238,10 @@ <ExtensionNode name = "Assembly" type = "MonoDevelop.Core.AddIns.AssemblyExtensionNode" /> </ExtensionPoint> + <ExtensionPoint path="/MonoDevelop/Ide/TypeService/OptionProviders"> + <ExtensionNode name="Class" /> + </ExtensionPoint> + <!-- Extensions --> <Extension path = "/MonoDevelop/Core/Applications"> @@ -401,6 +405,8 @@ </Extension> <Extension path="/MonoDevelop/Ide/Composition"> + <Assembly file="Microsoft.CodeAnalysis.CSharp.EditorFeatures.dll"/> + <Assembly file="Microsoft.CodeAnalysis.EditorFeatures.dll"/> <Assembly file="Microsoft.CodeAnalysis.Features.dll" /> <Assembly file="Microsoft.CodeAnalysis.Workspaces.dll" /> <Assembly file="Microsoft.CodeAnalysis.Workspaces.Desktop.dll" /> @@ -410,6 +416,8 @@ <Assembly file="Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll" /> <Assembly file="MonoDevelop.Ide.dll"/> <Assembly file="Microsoft.VisualStudio.Text.Implementation.dll"/> + <Assembly file="Microsoft.VisualStudio.Text.Logic.dll"/> + <Assembly file="Microsoft.VisualStudio.Text.UI.dll"/> <Assembly file="Microsoft.VisualStudio.Language.StandardClassification.dll"/> <Assembly file="Microsoft.CodeAnalysis.CSharp.EditorFeatures.dll" /> </Extension> diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/Templates.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/Templates.addin.xml index 8ac342586a..0de8c942f8 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/Templates.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/Templates.addin.xml @@ -35,11 +35,21 @@ <ExtensionNode name="Template" type="MonoDevelop.Ide.Codons.TemplateExtensionNode"/> </ExtensionPoint> +<ExtensionPoint path = "/MonoDevelop/Ide/ItemTemplates" name = "Microsoft Templating Engine file templates"> + <Description>Microsoft templating engine file templates.</Description> + <ExtensionNode name="Template" type="MonoDevelop.Ide.Codons.ItemTemplateExtensionNode"/> +</ExtensionPoint> + <ExtensionPoint path = "/MonoDevelop/Ide/ProjectTemplatePackageInstallers" name = "Project template package installers"> <Description>Installs packages defined in the project template. Must implement MonoDevelop.Ide.Templates.ProjectTemplatePackageInstaller</Description> <ExtensionNode name="Class" /> </ExtensionPoint> +<ExtensionPoint path = "/MonoDevelop/Ide/ItemTemplatePackageInstallers" name = "Item template package installers"> + <Description>Installs packages defined in the template. Must implement MonoDevelop.Ide.Templates.ItemTemplatePackageInstaller</Description> + <ExtensionNode name="Class" /> +</ExtensionPoint> + <ExtensionPoint path="/MonoDevelop/Ide/ProjectTemplatePackageRepositories"> <Description>Defines a path where NuGet packages are searched for when creating a project from a template.</Description> <ExtensionNode name="PackageRepository" type="MonoDevelop.Ide.Templates.PackageRepositoryNode" /> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperMac.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperMac.cs index 591fdc2de8..920f3ce669 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperMac.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperMac.cs @@ -466,6 +466,33 @@ namespace MonoDevelop.Components.AtkCocoaHelper nsa.AccessibilityChildren = newChildren; } + public static void TransferAccessibleChild (this Atk.Object from, Atk.Object to, Atk.Object child) + { + var fromNsa = GetNSAccessibilityElement (from); + var toNsa = GetNSAccessibilityElement (to); + var childNsa = GetNSAccessibilityElement (child); + + if (fromNsa == null || toNsa == null || childNsa == null) { + return; + } + + var fromChildren = fromNsa.AccessibilityChildren; + + if (fromChildren == null || fromChildren.Length == 0) { + return; + } + + var fromList = fromChildren.ToList (); + fromList.Remove ((NSObject) childNsa); + fromNsa.AccessibilityChildren = fromList.ToArray (); + + var toChildren = toNsa.AccessibilityChildren; + List<NSObject> toList = toChildren == null ? new List<NSObject> () : toChildren.ToList (); + + toList.Add ((NSObject)childNsa); + toNsa.AccessibilityChildren = toList.ToArray (); + } + public static void SetAccessibleChildren (this Atk.Object o, AccessibilityElementProxy [] children) { var nsa = GetNSAccessibilityElement (o); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperNoOp.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperNoOp.cs index b9bc3f936b..bcd62c1c87 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperNoOp.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AtkCocoaHelper/AtkCocoaHelperNoOp.cs @@ -157,6 +157,10 @@ namespace MonoDevelop.Components.AtkCocoaHelper public static void MakeAccessibilityAnnouncement (this Atk.Object o, string message) { } + + public static void TransferAccessibleChild (this Atk.Object from, Atk.Object to, Atk.Object child) + { + } } public class AccessibilityElementProxy : IAccessibilityElementProxy diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppResult.cs index 3f9665b513..4bce0b11d5 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppResult.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.AutoTest/AppResult.cs @@ -31,6 +31,7 @@ using System.Reflection; using System.Linq; using System.Collections.ObjectModel; using MonoDevelop.Components.AutoTest.Results; +using MonoDevelop.Core; namespace MonoDevelop.Components.AutoTest { @@ -140,9 +141,15 @@ namespace MonoDevelop.Components.AutoTest protected object GetPropertyValue (string propertyName, object requestedObject) { + if (requestedObject == null) { + LoggingService.LogError ("GetPropertyValue : requestedObject == null property requested : " + propertyName); + return null; + } return AutoTestService.CurrentSession.UnsafeSync (delegate { PropertyInfo propertyInfo = requestedObject.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); + if (propertyInfo == null) + LoggingService.LogError ($"GetPropertyValue : propertyName {propertyName} not found on object {requestedObject}."); if (propertyInfo != null && propertyInfo.CanRead && !propertyInfo.GetIndexParameters ().Any ()) { var propertyValue = propertyInfo.GetValue (requestedObject); if (propertyValue != null) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs index dc9ff17c2c..fbd6a1ed22 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/DockNotebookTab.cs @@ -79,10 +79,10 @@ namespace MonoDevelop.Components.DockNotebook Gdk.Rectangle cocoaFrame; // value is in the TabStrip's coordinate space, whereas we need to set the button in the tab space. - cocoaFrame.X = (int)value.X - allocation.X; - int halfParentWidth = allocation.Height / 2; - double dy = value.Y - halfParentWidth; - cocoaFrame.Y = (int) (halfParentWidth + dy) - allocation.Y; + cocoaFrame.X = (int)value.X;// - allocation.X; + int halfParentHeight = (int)(strip.Allocation.Height / 2); + double dy = value.Y - halfParentHeight; + cocoaFrame.Y = (int) ((halfParentHeight + dy) - ((int)value.Height / 2)); cocoaFrame.Width = (int) value.Width; cocoaFrame.Height = (int) value.Height; @@ -247,7 +247,6 @@ namespace MonoDevelop.Components.DockNotebook CloseButtonAccessible.PerformShowMenu += OnCloseButtonShowMenu; CloseButtonAccessible.Title = Core.GettextCatalog.GetString ("Close document"); CloseButtonAccessible.Identifier = "DockNotebook.Tab.CloseButton"; - Accessible.AddAccessibleChild (CloseButtonAccessible); } this.notebook = notebook; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs index 3feed3bf53..8fbd898711 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.DockNotebook/TabStrip.cs @@ -147,6 +147,9 @@ namespace MonoDevelop.Components.DockNotebook if (notebook == null) throw new ArgumentNullException ("notebook"); + Accessible.SetCommonAttributes ("Document.Tabstrip", + Core.GettextCatalog.GetString ("Document Navigation Bar"), + Core.GettextCatalog.GetString ("Contains controls to select which document is being edited")); Accessible.SetRole (AtkCocoa.Roles.AXTabGroup); // Handle focus for the tabs. @@ -254,7 +257,8 @@ namespace MonoDevelop.Components.DockNotebook var tab = args.Tab;
if (tab.Accessible != null) {
- Accessible.AddAccessibleElement (tab.Accessible);
+ Accessible.AddAccessibleElement (tab.Accessible); + Accessible.AddAccessibleElement (tab.CloseButtonAccessible);
tab.AccessibilityPressTab += OnAccessibilityPressTab;
tab.AccessibilityPressCloseButton += OnAccessibilityPressCloseButton;
@@ -276,6 +280,7 @@ namespace MonoDevelop.Components.DockNotebook tab.AccessibilityShowMenu -= OnAccessibilityShowMenu;
Accessible.RemoveAccessibleElement (tab.Accessible); + Accessible.RemoveAccessibleElement (tab.CloseButtonAccessible); } tab.Dispose (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs index 2eb346a6ae..428943c8ae 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItem.cs @@ -32,6 +32,8 @@ using System; using System.Xml; using Mono.Unix; +using MonoDevelop.Components.AtkCocoaHelper; + namespace MonoDevelop.Components.Docking { public class DockItem @@ -87,7 +89,8 @@ namespace MonoDevelop.Components.Docking get { return stickyVisible; } set { stickyVisible = value; } } - + + internal event EventHandler LabelChanged; public string Label { get { return label ?? string.Empty; } set { @@ -97,6 +100,13 @@ namespace MonoDevelop.Components.Docking frame.UpdateTitle (this); if (floatingWindow != null) floatingWindow.Title = GetWindowTitle (); + + toolbarTop?.UpdateAccessibilityLabel (); + toolbarLeft?.UpdateAccessibilityLabel (); + toolbarRight?.UpdateAccessibilityLabel (); + toolbarBottom?.UpdateAccessibilityLabel (); + + LabelChanged?.Invoke (this, EventArgs.Empty); } } @@ -123,6 +133,10 @@ namespace MonoDevelop.Components.Docking titleTab.VisualStyle = currentVisualStyle; titleTab.SetLabel (Widget, icon, label); titleTab.ShowAll (); + + if (widget != null) { + titleTab.Accessible.AddLinkedUIElement (widget.Accessible); + } } return titleTab; } @@ -144,6 +158,10 @@ namespace MonoDevelop.Components.Docking widget.VisualStyle = currentVisualStyle; widget.Visible = false; // Required to ensure that the Shown event is fired widget.Shown += SetupContent; + + if (titleTab != null) { + titleTab.Accessible.AddLinkedUIElement (titleTab.Accessible); + } } return widget; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs index 3aae1c9242..45a896b436 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemContainer.cs @@ -47,25 +47,27 @@ namespace MonoDevelop.Components.Docking public DockItemContainer (DockFrame frame, DockItem item) { this.item = item; + item.LabelChanged += UpdateAccessibilityLabel; mainBox = new VBox (); - mainBox.Accessible.SetShouldIgnore (true); + mainBox.Accessible.SetShouldIgnore (false); + UpdateAccessibilityLabel (null, null); Add (mainBox); mainBox.ResizeMode = Gtk.ResizeMode.Queue; mainBox.Spacing = 0; ShowAll (); - + mainBox.PackStart (item.GetToolbar (DockPositionType.Top).Container, false, false, 0); HBox hbox = new HBox (); - hbox.Accessible.SetShouldIgnore (true); + hbox.Accessible.SetTitle ("Hbox"); hbox.Show (); hbox.PackStart (item.GetToolbar (DockPositionType.Left).Container, false, false, 0); contentBox = new HBox (); - contentBox.Accessible.SetShouldIgnore (true); + hbox.Accessible.SetTitle ("Content"); contentBox.Show (); hbox.PackStart (contentBox, true, true, 0); @@ -76,6 +78,11 @@ namespace MonoDevelop.Components.Docking mainBox.PackStart (item.GetToolbar (DockPositionType.Bottom).Container, false, false, 0); } + void UpdateAccessibilityLabel (object sender, EventArgs args) + { + mainBox.Accessible.SetTitle (Core.GettextCatalog.GetString ("{0} Pad", item.Label)); + } + DockVisualStyle visualStyle; public DockVisualStyle VisualStyle { @@ -91,7 +98,13 @@ namespace MonoDevelop.Components.Docking item.Status = DockItemStatus.AutoHide; } - public void UpdateContent () + protected override void OnDestroyed() + { + item.LabelChanged -= UpdateAccessibilityLabel; + base.OnDestroyed(); + } + + public void UpdateContent () { if (widget != null) ((Gtk.Container)widget.Parent).Remove (widget); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs index a9b6a3b919..d4532109ac 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemTitleTab.cs @@ -105,8 +105,7 @@ namespace MonoDevelop.Components.Docking actionHandler.PerformPress += HandlePress; actionHandler.PerformShowMenu += HandleShowMenu; - Accessible.SetRole (AtkCocoa.Roles.AXGroup, "pad header"); - Accessible.SetSubRole ("XAPadHeader"); + UpdateRole (false, null); CanFocus = true; this.item = item; @@ -123,6 +122,36 @@ namespace MonoDevelop.Components.Docking subscribedLeaveEvent = this.SubscribeLeaveEvent (OnLeave); } + internal void UpdateRole (bool isTab, TabStrip strip) + { + Atk.Object fromAccessible = null, toAccessible = null; + + if (!isTab) { + Accessible.SetRole (AtkCocoa.Roles.AXGroup, "pad header"); + Accessible.SetSubRole ("XAPadHeader"); + + // Take the button accessibles back from the strip + if (strip != null) { + fromAccessible = strip.Accessible; + toAccessible = Accessible; + } + } else { + Accessible.SetRole (AtkCocoa.Roles.AXRadioButton, "tab"); + Accessible.SetSubRole (""); + + // Give the button accessibles to the strip + if (strip != null) { + fromAccessible = Accessible; + toAccessible = strip.Accessible; + } + } + + if (fromAccessible != null && toAccessible != null) { + fromAccessible.TransferAccessibleChild (toAccessible, btnDock.Accessible); + fromAccessible.TransferAccessibleChild (toAccessible, btnClose.Accessible); + } + } + public DockVisualStyle VisualStyle { get { return visualStyle; } set { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemToolbar.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemToolbar.cs index 5b0340cf57..f8fe43fc83 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemToolbar.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/DockItemToolbar.cs @@ -28,6 +28,8 @@ using System; using System.Linq; using Gtk; +using MonoDevelop.Components.AtkCocoaHelper; + namespace MonoDevelop.Components.Docking { public class DockItemToolbar @@ -76,6 +78,35 @@ namespace MonoDevelop.Components.Docking topFrame.Add (box); // topFrame.GradientBackround = true; + + box.Accessible.SetShouldIgnore (false); + box.Accessible.Role = Atk.Role.ToolBar; + + UpdateAccessibilityLabel (); + } + + internal void UpdateAccessibilityLabel () + { + string name = ""; + switch (position) { + case DockPositionType.Bottom: + name = Core.GettextCatalog.GetString ("Bottom {0} pad toolbar", parentItem.Label); + break; + + case DockPositionType.Left: + name = Core.GettextCatalog.GetString ("Left {0} pad toolbar", parentItem.Label); + break; + + case DockPositionType.Right: + name = Core.GettextCatalog.GetString ("Right {0} pad toolbar", parentItem.Label); + break; + + case DockPositionType.Top: + name = Core.GettextCatalog.GetString ("Top {0} pad toolbar", parentItem.Label); + break; + } + + box.Accessible.SetCommonAttributes ("padtoolbar", name, ""); } internal void SetStyle (DockVisualStyle style) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs index f3dc20616d..89de00dbc1 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Docking/TabStrip.cs @@ -49,6 +49,9 @@ namespace MonoDevelop.Components.Docking public TabStrip (DockFrame frame) { Accessible.SetRole (AtkCocoa.Roles.AXTabGroup); + Accessible.SetCommonAttributes ("Docking.TabStrip", + GettextCatalog.GetString ("Pad Tab Bar"), + GettextCatalog.GetString ("The different pads in this dock position")); VBox vbox = new VBox (); vbox.Accessible.SetShouldIgnore (true); @@ -95,12 +98,16 @@ namespace MonoDevelop.Components.Docking } tab.TabPressed += OnTabPress; + tab.UpdateRole (true, this); + UpdateAccessibilityTabs (); } void HandleRemoved (object o, RemovedArgs args) { var w = (DockItemTitleTab)args.Widget; + w.UpdateRole (false, this); + w.TabPressed -= OnTabPress; if (currentTab >= box.Children.Length) currentTab = box.Children.Length - 1; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs index 0140114a63..721faff88e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs @@ -1092,7 +1092,7 @@ namespace MonoDevelop.Components.MainToolbar int i = s.IndexOf ('_'); if (i == -1) return s; - var sb = new StringBuilder (i); + var sb = StringBuilderCache.Allocate (); sb.Append (s, 0, i); for (; i < s.Length; i++) { if (s [i] == '_') { @@ -1102,7 +1102,7 @@ namespace MonoDevelop.Components.MainToolbar } sb.Append (s [i]); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs index c44cd39d56..c6e6c03918 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs @@ -106,7 +106,7 @@ namespace MonoDevelop.Components.MainToolbar { var lane = StringMatcher.GetMatcher (toMatch, true).GetMatch (text);
var matchHexColor = selected ? selectedResultMatchTextColor : resultMatchTextColor; - StringBuilder result = new StringBuilder (text.Length + matchHexColor.Length + 46); + var result = StringBuilderCache.Allocate (); if (lane != null) { int lastPos = 0; for (int n=0; n < lane.Length; n++) { @@ -125,7 +125,7 @@ namespace MonoDevelop.Components.MainToolbar } else { MarkupUtilities.AppendEscapedString (result, text, 0, text.Length); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public virtual bool CanActivate { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs index 30706dedee..34f8345a9c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs @@ -157,7 +157,8 @@ namespace MonoDevelop.Components public NSContextMenuItem (string label, ContextMenuItem item) : base (label) { contextMenu = new WeakReference<ContextMenuItem> (item); - this.Activated += OnActivated; + if (item.SubMenu == null || item.SubMenu.Items.Count == 0) + this.Activated += OnActivated; } static void OnActivated (object sender, EventArgs args) @@ -195,6 +196,7 @@ namespace MonoDevelop.Components public NSLocationAwareMenu (ContextMenu menu, Action closeHandler, NSLocationAwareMenu parent) { WeakDelegate = new ContextMenuDelegate (menu) { CloseHandler = closeHandler }; + Parent = parent != null ? new WeakReference<NSLocationAwareMenu> (parent) : null; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkUtil.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkUtil.cs index f1f8f2aea3..c3a176f3db 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkUtil.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkUtil.cs @@ -945,12 +945,16 @@ namespace MonoDevelop.Components { TreeViewColumn col; TreeView tree; + TreePath path; TreeIter iter; + TreeStore treeStore; public CellTooltipWindow (TreeView tree, TreeViewColumn col, TreePath path) { this.tree = tree; this.col = col; + this.treeStore = tree.Model as TreeStore; + this.path = path; NudgeHorizontal = true; @@ -991,6 +995,12 @@ namespace MonoDevelop.Components bool hasFgColor = false; int x = 1; + // Make sure that the row has not been removed inbetween. + // If the model is a TreeStore, it can do the validation for us, otherwise we need to validate the path. + if ((treeStore != null && treeStore.IterIsValid (iter) == false) || !tree.Model.GetIter (out iter, path)) { + GtkUtil.HideTooltip (tree); + return true; + } col.CellSetCellData (tree.Model, iter, false, false); foreach (CellRenderer cr in col.CellRenderers) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/MDMenu.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/MDMenu.cs index 8d2882e059..793ab813f3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/MDMenu.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/MDMenu.cs @@ -82,7 +82,7 @@ namespace MonoDevelop.Components.Mac continue; } - Command cmd = manager.GetCommand (ce.CommandId); + Command cmd = ce.GetCommand (manager); if (cmd == null) { LoggingService.LogError ("MacMenu: '{0}' maps to null command", ce.CommandId); continue; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionController.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionController.cs index 1263f0f5a7..00e5de7e0c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionController.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionController.cs @@ -1,4 +1,4 @@ -ο»Ώ// +// // CodeCompletionSession.cs // // Author: @@ -32,6 +32,7 @@ using Gtk; using MonoDevelop.Ide.Editor.Extension; using Xwt.Drawing; using MonoDevelop.Core; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.CodeCompletion { @@ -205,7 +206,6 @@ namespace MonoDevelop.Ide.CodeCompletion initialWordLength = CompletionWidget.SelectedLength > 0 ? 0 : text.Length; StartOffset = CompletionWidget.CaretOffset - initialWordLength; - HideWhenWordDeleted = initialWordLength != 0; ResetSizes (); UpdateWordSelection (); @@ -259,7 +259,6 @@ namespace MonoDevelop.Ide.CodeCompletion { usingPreviewEntry = false; previewCompletionEntryText = ""; - HideWhenWordDeleted = false; SelectedItemCompletionText = null; ResetViewState(); } @@ -505,10 +504,6 @@ namespace MonoDevelop.Ide.CodeCompletion get { return initialWordLength; } } - bool HideWhenWordDeleted { - get; set; - } - public CompletionTextEditorExtension Extension { get; set; @@ -710,15 +705,7 @@ namespace MonoDevelop.Ide.CodeCompletion set; } - int startOffset; - internal int StartOffset { - get { - return startOffset; - } - set { - startOffset = value; - } - } + internal int StartOffset { get; set; } public int EndOffset { get; @@ -1058,13 +1045,10 @@ namespace MonoDevelop.Ide.CodeCompletion public KeyActions PostProcessKey (KeyDescriptor descriptor) { - if (CompletionWidget == null || StartOffset > CompletionWidget.CaretOffset) {// CompletionWidget == null may happen in unit tests. + if (CompletionWidget == null) {// CompletionWidget == null may happen in unit tests. return KeyActions.CloseWindow | KeyActions.Process; } - if (HideWhenWordDeleted && StartOffset >= CompletionWidget.CaretOffset) { - return KeyActions.CloseWindow | KeyActions.Process; - } switch (descriptor.SpecialKey) { case SpecialKey.BackSpace: ResetSizes (); @@ -1166,7 +1150,7 @@ namespace MonoDevelop.Ide.CodeCompletion void UpdateLastWordChar () { if (CompletionWidget != null) - EndOffset = CompletionWidget.CaretOffset; + EndOffset = Math.Max (StartOffset, CompletionWidget.CaretOffset); } void SelectEntry (string s) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionPresenterSession.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionPresenterSession.cs deleted file mode 100644 index 8e760d05d1..0000000000 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionPresenterSession.cs +++ /dev/null @@ -1,92 +0,0 @@ -ο»Ώ// -// CompletionPresenterSession.cs -// -// Author: -// Mike KrΓΌger <mikkrg@microsoft.com> -// -// Copyright (c) 2017 Microsoft Corporation -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Completion; -using Microsoft.VisualStudio.Text; - -namespace MonoDevelop.Ide.CodeCompletion -{ - abstract class CompletionPresenterSession - { - protected abstract RoslynCompletionData WrapItem (CompletionItem item); - - public void PresentItems (ITrackingSpan triggerSpan, IList<CompletionItem> items, CompletionItem selectedItem, CompletionItem suggestionModeItem, bool suggestionMode, bool isSoftSelected, ImmutableArray<CompletionItemFilter> completionItemFilters, string filterText) - { - var result = new CompletionDataList (); - - foreach (var item in items) { - if (string.IsNullOrEmpty (item.DisplayText)) - continue; - result.Add (WrapItem (item)); - } - if (suggestionMode) - result.AutoSelect = false; - if (filterText != null) - result.DefaultCompletionString = filterText; - if (suggestionModeItem != null) { - result.DefaultCompletionString = suggestionModeItem.DisplayText; - result.AutoSelect = false; - } - - if (selectedItem != null) { - result.DefaultCompletionString = selectedItem.DisplayText; - } - - // TODO: isSoftSelected - // TODO: completionItemFilters - var editor = IdeApp.Workbench.ActiveDocument.Editor; - var widget = IdeApp.Workbench.ActiveDocument.GetContent<ICompletionWidget> (); - CompletionWindowManager.ShowWindow (null, (char)0, result, widget, widget.CreateCodeCompletionContext (editor.CaretOffset)); - } - - public void SelectPreviousItem () - { - CompletionWindowManager.PreProcessKeyEvent (Editor.Extension.KeyDescriptor.Up); - } - - public void SelectNextItem () - { - CompletionWindowManager.PreProcessKeyEvent (Editor.Extension.KeyDescriptor.Down); - } - - public void SelectPreviousPageItem () - { - CompletionWindowManager.PreProcessKeyEvent (Editor.Extension.KeyDescriptor.PageUp); - } - - public void SelectNextPageItem () - { - CompletionWindowManager.PreProcessKeyEvent (Editor.Extension.KeyDescriptor.PageDown); - } - - // public event EventHandler<CompletionItemEventArgs> ItemSelected; - // public event EventHandler<CompletionItemEventArgs> ItemCommitted; - //public event EventHandler<CompletionItemFilterStateChangedEventArgs> FilterStateChanged; - } -} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionWindowManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionWindowManager.cs index df602402bb..96dea0addf 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionWindowManager.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionWindowManager.cs @@ -28,6 +28,7 @@ using System; using MonoDevelop.Core; using MonoDevelop.Ide.Gui.Content; using MonoDevelop.Ide.Editor.Extension; +using MonoDevelop.Ide.Editor; namespace MonoDevelop.Ide.CodeCompletion { @@ -176,15 +177,29 @@ namespace MonoDevelop.Ide.CodeCompletion return wnd.PreProcessKeyEvent (descriptor); } + static bool isInUpdate; public static void UpdateCursorPosition () { if (!IsVisible) return; - if (wnd.IsInCompletion || isShowing) + if (wnd.IsInCompletion || isShowing || isInUpdate) return; - var caretOffset = wnd.CompletionWidget.CaretOffset; - if (caretOffset < wnd.StartOffset || caretOffset > wnd.EndOffset + 1) { - HideWindow (); + isInUpdate = true; + try { + var widget = wnd.CompletionWidget; + if (widget == null) + return; + var impl = widget as ITextEditorImpl; + if (impl != null) + impl.EnsureCaretIsNotVirtual (); + var caretOffset = widget.CaretOffset; + if (caretOffset < wnd.StartOffset || caretOffset > wnd.EndOffset + 1) { + HideWindow (); + } + if (impl != null) + impl.FixVirtualIndentation (); + } finally { + isInUpdate = false; } } @@ -213,8 +228,9 @@ namespace MonoDevelop.Ide.CodeCompletion public static void HideWindow () { isShowing = false; - if (IsVisible) + if (IsVisible) { wnd.HideWindow (); + } } static void HandleWndVisibleChanged (object sender, EventArgs args) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs index f8dde4354a..0dd6926ff2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs @@ -82,22 +82,26 @@ namespace MonoDevelop.Ide.CodeCompletion protected abstract string MimeType { get; } public override IconId Icon { - get { - if (CompletionItem.Tags.Contains ("Snippet")) { - var template = CodeTemplateService.GetCodeTemplates (MimeType).FirstOrDefault (t => t.Shortcut == CompletionItem.DisplayText); - if (template != null) - return template.Icon; - } - var modifier = GetItemModifier (); - var type = GetItemType (); - var hash = modifier | type << 16; - if (!IconIdCache.ContainsKey (hash)) - IconIdCache [hash] = "md-" + modifierType[modifier] + completionType[type]; - return IconIdCache [hash]; + get => GetIcon (CompletionItem, MimeType); + } + + + internal static string GetIcon (CompletionItem completionItem, string mimeType) + { + if (completionItem.Tags.Contains ("Snippet")) { + var template = CodeTemplateService.GetCodeTemplates (mimeType).FirstOrDefault (t => t.Shortcut == completionItem.DisplayText); + if (template != null) + return template.Icon; } + var modifier = GetItemModifier (completionItem); + var type = GetItemType (completionItem); + var hash = modifier | type << 16; + if (!IconIdCache.ContainsKey (hash)) + IconIdCache [hash] = "md-" + modifierType [modifier] + completionType [type]; + return IconIdCache [hash]; } - static Dictionary<int, string> IconIdCache = new Dictionary<int, string>(); + static Dictionary<int, string> IconIdCache = new Dictionary<int, string> (); public RoslynCompletionData (Microsoft.CodeAnalysis.Document document, ITextSnapshot triggerSnapshot, CompletionService completionService, CompletionItem completionItem) { @@ -121,7 +125,7 @@ namespace MonoDevelop.Ide.CodeCompletion return null; } - string [] completionType = { + readonly static string [] completionType = { "field", "literal", "variable", @@ -140,7 +144,7 @@ namespace MonoDevelop.Ide.CodeCompletion "extensionmethod" }; - static Dictionary<string, int> roslynCompletionTypeTable = new Dictionary<string, int> { + readonly static Dictionary<string, int> roslynCompletionTypeTable = new Dictionary<string, int> { { "Field", 0 }, { "Alias", 0 }, { "ArrayType", 0 }, @@ -188,17 +192,17 @@ namespace MonoDevelop.Ide.CodeCompletion { "ExtensionMethod", 15 } }; - int GetItemType () + static int GetItemType (CompletionItem completionItem) { - foreach (var tag in CompletionItem.Tags) { + foreach (var tag in completionItem.Tags) { if (roslynCompletionTypeTable.TryGetValue (tag, out int result)) return result; } - LoggingService.LogWarning ("RoslynCompletionData: Can't find item type '" + string.Join (",", CompletionItem.Tags) + "'"); + LoggingService.LogWarning ("RoslynCompletionData: Can't find item type '" + string.Join (",", completionItem.Tags) + "'"); return 1; } - string[] modifierType = { + readonly static string [] modifierType = { "", "private-", "ProtectedOrInternal-", @@ -208,7 +212,7 @@ namespace MonoDevelop.Ide.CodeCompletion }; - static Dictionary<string, int> modifierTypeTable = new Dictionary<string, int> { + readonly static Dictionary<string, int> modifierTypeTable = new Dictionary<string, int> { { "Private", 1 }, { "ProtectedAndInternal", 2 }, { "Protected", 3 }, @@ -216,9 +220,9 @@ namespace MonoDevelop.Ide.CodeCompletion { "ProtectedOrInternal", 5 } }; - int GetItemModifier () + static int GetItemModifier (CompletionItem completionItem) { - foreach (var tag in CompletionItem.Tags) { + foreach (var tag in completionItem.Tags) { if (modifierTypeTable.TryGetValue (tag, out int result)) return result; } @@ -257,7 +261,7 @@ namespace MonoDevelop.Ide.CodeCompletion editor.ReplaceText (mappedSpan.Start + 1, mappedSpan.Length - 1, completionChange.TextChange.NewText); } else editor.ReplaceText (mappedSpan.Start, mappedSpan.Length, completionChange.TextChange.NewText); - + if (completionChange.NewPosition.HasValue) editor.CaretOffset = completionChange.NewPosition.Value; @@ -271,10 +275,23 @@ namespace MonoDevelop.Ide.CodeCompletion protected abstract void Format (TextEditor editor, Gui.Document document, int start, int end); - public override async Task<TooltipInformation> CreateTooltipInformation (bool smartWrap, CancellationToken cancelToken) + public override Task<TooltipInformation> CreateTooltipInformation (bool smartWrap, CancellationToken cancelToken) + { + return CreateTooltipInformation (doc, CompletionItem, smartWrap, cancelToken); + } + + internal static async Task<TooltipInformation> CreateTooltipInformation (Microsoft.CodeAnalysis.Document doc, CompletionItem CompletionItem, bool smartWrap, CancellationToken cancelToken) { - var description = await Task.Run (() => completionService.GetDescriptionAsync (doc, CompletionItem)).ConfigureAwait (false); - var markup = new StringBuilder (); + CompletionDescription description; + var completionService = doc.Project.Solution.Workspace.Services.GetLanguageServices (doc.Project.Language).GetService<CompletionService> (); + if (completionService == null) + return null; + if (CommonCompletionItem.HasDescription (CompletionItem)) { + description = CommonCompletionItem.GetDescription (CompletionItem); + } else { + description = await Task.Run (() => completionService.GetDescriptionAsync (doc, CompletionItem)).ConfigureAwait (false); + } + var markup = StringBuilderCache.Allocate (); var theme = SyntaxHighlightingService.GetIdeFittingTheme (DefaultSourceEditorOptions.Instance.GetEditorTheme ()); var taggedParts = description.TaggedParts; int i = 0; @@ -294,9 +311,27 @@ namespace MonoDevelop.Ide.CodeCompletion markup.Append ("</span>"); } return new TooltipInformation { - SignatureMarkup = markup.ToString () + SignatureMarkup = StringBuilderCache.ReturnAndFree (markup) }; } - } -} + public override bool IsCommitCharacter (char keyChar, string partialWord) + { + foreach (var rule in CompletionItem.Rules.CommitCharacterRules) { + switch (rule.Kind) { + case CharacterSetModificationKind.Add: + if (rule.Characters.Contains (keyChar)) + return true; + continue; + case CharacterSetModificationKind.Remove: + if (rule.Characters.Contains (keyChar)) + return false; + continue; + case CharacterSetModificationKind.Replace: + return rule.Characters.Contains (keyChar); + } + } + return base.IsCommitCharacter (keyChar, partialWord); + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs index 02874129d6..1dc585dcf8 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs @@ -244,7 +244,7 @@ namespace MonoDevelop.Ide.CodeTemplates { var expansion = CodeTemplateService.GetExpansionObject (this); var result = new TemplateResult (); - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); int lastOffset = 0; string code = context.Editor.FormatString (context.InsertPosition, context.TemplateCode); result.TextLinks = new List<TextLink> (); @@ -339,7 +339,7 @@ namespace MonoDevelop.Ide.CodeTemplates // format & indent template code var data = TextEditorFactory.CreateNewDocument (); - data.Text = sb.ToString (); + data.Text = StringBuilderCache.ReturnAndFree (sb); data.TextChanged += delegate(object sender, MonoDevelop.Core.Text.TextChangeEventArgs e) {
for (int i = 0; i < e.TextChanges.Count; ++i) {
var change = e.TextChanges[i]; @@ -369,7 +369,7 @@ namespace MonoDevelop.Ide.CodeTemplates public string IndentCode (string code, string eol, string indent) { - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); for (int i = 0; i < code.Length; i++) { switch (code[i]) { case '\r': @@ -385,7 +385,7 @@ namespace MonoDevelop.Ide.CodeTemplates break; } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } static void IndentCode (ITextDocument data, string lineIndent) @@ -404,38 +404,38 @@ namespace MonoDevelop.Ide.CodeTemplates while (i >= 0 && !Char.IsWhiteSpace (str[i])) { i--; } - var indent = new StringBuilder (); + var indent = StringBuilderCache.Allocate (); while (i >= 0 && (str[i] == ' ' || str[i] == '\t')) { indent.Append (str[i]); i--; } - return indent.ToString (); + return StringBuilderCache.ReturnAndFree (indent); } string RemoveIndent (string text, string indent) { var doc = TextEditorFactory.CreateNewDocument (); doc.Text = text; - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); foreach (var line in doc.GetLines ()) { string curLineIndent = line.GetIndentation (doc); int offset = Math.Min (curLineIndent.Length, indent.Length); result.Append (doc.GetTextBetween (line.Offset + offset, line.EndOffsetIncludingDelimiter)); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } string Reindent (string text, string indent) { var doc = TextEditorFactory.CreateNewDocument (); doc.Text = text; - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); foreach (var line in doc.GetLines ()) { if (result.Length > 0) result.Append (indent); result.Append (doc.GetTextAt (line.SegmentIncludingDelimiter)); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public void Insert (MonoDevelop.Ide.Gui.Document document) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateService.cs index f8556dbc51..043c670ebe 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateService.cs @@ -122,8 +122,8 @@ namespace MonoDevelop.Ide.CodeTemplates { var result = new CodeTemplate (); result.Shortcut = setting.TabTrigger; - var sb = new StringBuilder (); - var nameBuilder = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); + var nameBuilder = StringBuilderCache.Allocate (); bool readDollar = false; bool inBracketExpression = false; bool inExpressionContent = false; @@ -194,8 +194,8 @@ namespace MonoDevelop.Ide.CodeTemplates nameBuilder.Length = 0; inVariable = false; } - - result.Code = sb.ToString (); + StringBuilderCache.Free (nameBuilder); + result.Code = StringBuilderCache.ReturnAndFree (sb); result.CodeTemplateContext = CodeTemplateContext.Standard; result.CodeTemplateType = CodeTemplateType.Expansion; result.Description = setting.Name; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Codons/ItemTemplateExtensionNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Codons/ItemTemplateExtensionNode.cs new file mode 100644 index 0000000000..5da1f66ff8 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Codons/ItemTemplateExtensionNode.cs @@ -0,0 +1,80 @@ +ο»Ώ// +// ItemTemplateExtensionNode.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using Mono.Addins; + +namespace MonoDevelop.Ide.Codons +{ + [ExtensionNode (Description = "A file template")] + class ItemTemplateExtensionNode : ExtensionNode + { + [NodeAttribute ("path", "A .nupkg file or a folder.")] + string path; + + public string ScanPath { + get { + return Addin.GetFilePath (path); + } + } + + [NodeAttribute ("templateId", "Overrides the template id from the extension node id. Allows the same template to be used with different parameters.")] + string templateId; + + public string TemplateId { + get { + return templateId ?? Id; + } + } + + [NodeAttribute ("_overrideName", "Override name used in template.json file.", Localizable = true)] + string overrideName; + + public string OverrideName { + get { + return overrideName; + } + } + + [NodeAttribute ("defaultParameters", "Default parameters for template.")] + string defaultParameters; + + public string DefaultParameters { + get { + return defaultParameters; + } + } + + [NodeAttribute ("supportedParameters", "Parameters supported by the template.")] + string supportedParameters; + + public string SupportedParameters { + get { + return supportedParameters; + } + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CommonEditorAssetServiceFactory.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CommonEditorAssetServiceFactory.cs new file mode 100644 index 0000000000..d2e2fd09d9 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CommonEditorAssetServiceFactory.cs @@ -0,0 +1,41 @@ +using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Editor;
+using Microsoft.CodeAnalysis.Editor.Host;
+using Microsoft.CodeAnalysis.FindUsages;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.VisualStudio.Editor;
+using Microsoft.VisualStudio.Text;
+
+namespace MonoDevelop.Ide.Composition
+{
+ [Export (typeof (ICommonEditorAssetServiceFactory))]
+ internal class CommonEditorAssetServiceFactory : ICommonEditorAssetServiceFactory
+ {
+ public ICommonEditorAssetService GetOrCreate (ITextBuffer textBuffer)
+ {
+ return new CommonEditorAssetService (textBuffer);
+ }
+ }
+
+ public class CommonEditorAssetService : ICommonEditorAssetService
+ {
+ private ITextBuffer textBuffer;
+
+ public CommonEditorAssetService (ITextBuffer textBuffer)
+ {
+ this.textBuffer = textBuffer;
+ }
+
+ public T FindAsset<T> (Predicate<ICommonEditorAssetMetadata> isMatch = null) where T : class
+ {
+ return default(T);
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs index 70ac6f0610..5f4a061be3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs @@ -128,21 +128,25 @@ namespace MonoDevelop.Ide.Composition var discoveryErrors = catalog.DiscoveredParts.DiscoveryErrors; if (!discoveryErrors.IsEmpty) { - throw new ApplicationException ($"MEF catalog scanning errors encountered.\n{string.Join ("\n", discoveryErrors)}"); + foreach (var error in discoveryErrors) { + LoggingService.LogInfo ("MEF discovery error", error); + } + + // throw new ApplicationException ("MEF discovery errors"); } CompositionConfiguration configuration = CompositionConfiguration.Create (catalog); - if (!configuration.CompositionErrors.IsEmpty) {
- // capture the errors in an array for easier debugging
- var errors = configuration.CompositionErrors.ToArray (); + if (!configuration.CompositionErrors.IsEmpty) { + // capture the errors in an array for easier debugging + var errors = configuration.CompositionErrors.SelectMany (e => e).ToArray (); + foreach (var error in errors) { + LoggingService.LogInfo ("MEF composition error: " + error.Message); + } // For now while we're still transitioning to VSMEF it's useful to work // even if the composition has some errors. TODO: re-enable this. - //var messages = errors.SelectMany (e => e).Select (e => e.Message); - //var text = string.Join (Environment.NewLine, messages); - //Xwt.Clipboard.SetText (text); - //configuration.ThrowOnErrors ();
+ //configuration.ThrowOnErrors (); } RuntimeComposition = RuntimeComposition.CreateRuntimeComposition (configuration); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/InlineRenameService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/InlineRenameService.cs new file mode 100644 index 0000000000..a13d665518 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/InlineRenameService.cs @@ -0,0 +1,24 @@ +using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Editor;
+using Microsoft.CodeAnalysis.Text;
+
+namespace MonoDevelop.Ide.Composition
+{
+ [Export(typeof(IInlineRenameService))]
+ internal class InlineRenameService : IInlineRenameService
+ {
+ public IInlineRenameSession ActiveSession => null;
+
+ public InlineRenameSessionInfo StartInlineSession (Document document, TextSpan triggerSpan, CancellationToken cancellationToken = default (CancellationToken))
+ {
+ throw new NotImplementedException ();
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/JoinableTaskContextHost.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/JoinableTaskContextHost.cs index 303b5a01f3..cd0b7f82b2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/JoinableTaskContextHost.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/JoinableTaskContextHost.cs @@ -43,10 +43,7 @@ namespace MonoDevelop.Ide.Composition [ImportingConstructor]
public JoinableTaskContextHost ()
{
- Runtime.RunInMainThread (() => {
- var joinableTaskContext = new JoinableTaskContext ();
- this.JoinableTaskContext = joinableTaskContext;
- });
+ JoinableTaskContext = new JoinableTaskContext (Runtime.MainThread, Runtime.MainSynchronizationContext);
}
}
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/PlatformCatalog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/PlatformCatalog.cs index c0148e3a65..2e8163e388 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/PlatformCatalog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/PlatformCatalog.cs @@ -94,7 +94,7 @@ namespace Microsoft.VisualStudio.Platform return mimeType;
}
- return null;
+ return (ContentTypeRegistryService as IContentTypeRegistryService2).GetMimeType (type);
}
public IContentType GetContentType(string type)
@@ -105,7 +105,7 @@ namespace Microsoft.VisualStudio.Platform return contentType;
}
- return null;
+ return (ContentTypeRegistryService as IContentTypeRegistryService2).GetContentTypeForMimeType (type);
}
public void LinkTypes(string mimeType, IContentType contentType)
@@ -136,14 +136,6 @@ namespace Microsoft.VisualStudio.Platform {
LinkTypes ("text/plain", "text");
LinkTypes ("text/x-csharp", "csharp");
-
- if (this.ContentTypeRegistryService.GetContentType ("css") != null) {
- LinkTypes ("text/x-css", "css");
- LinkTypes ("text/x-less-web", "LESS");
- LinkTypes ("text/x-scss-web", "SCSS");
- LinkTypes ("text/x-html", "htmlx");
- LinkTypes ("text/x-json", "JSON");
- }
}
Tuple<ImmutableDictionary<string, IContentType>, ImmutableDictionary<IContentType, string>> maps = Tuple.Create(ImmutableDictionary<string, IContentType>.Empty, ImmutableDictionary<IContentType, string>.Empty);
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/StreamingFindUsagesPresenter.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/StreamingFindUsagesPresenter.cs new file mode 100644 index 0000000000..bc0c1a1dfc --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/StreamingFindUsagesPresenter.cs @@ -0,0 +1,29 @@ +using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Editor;
+using Microsoft.CodeAnalysis.Editor.Host;
+using Microsoft.CodeAnalysis.FindUsages;
+using Microsoft.CodeAnalysis.Text;
+
+namespace MonoDevelop.Ide.Composition
+{
+ [Export (typeof (IStreamingFindUsagesPresenter))]
+ internal class StreamingFindUsagesPresenter : IStreamingFindUsagesPresenter
+ {
+ public void ClearAll ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ public FindUsagesContext StartSearch (string title, bool supportsReferences)
+ {
+ throw new NotImplementedException ();
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs index 486c780048..137851a310 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Desktop/PlatformService.cs @@ -34,6 +34,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Text.RegularExpressions; +using Microsoft.VisualStudio.Platform;
+using Microsoft.VisualStudio.Utilities; + using Mono.Addins; using MonoDevelop.Core; using Mono.Unix; @@ -41,8 +44,8 @@ using MonoDevelop.Ide.Extensions; using MonoDevelop.Core.Execution; using MonoDevelop.Components; using MonoDevelop.Components.MainToolbar; - - +using MonoDevelop.Ide.Composition;
+
namespace MonoDevelop.Ide.Desktop { public abstract class PlatformService @@ -296,6 +299,23 @@ namespace MonoDevelop.Ide.Desktop MimeTypeNode FindMimeTypeForFile (string fileName) { + IFilePathRegistryService filePathRegistryService = CompositionManager.GetExportedValue<IFilePathRegistryService> ();
+ + try { + IContentType contentType = filePathRegistryService.GetContentTypeForPath (fileName); + if (contentType != PlatformCatalog.Instance.ContentTypeRegistryService.UnknownContentType) { + string mimeType = PlatformCatalog.Instance.MimeToContentTypeRegistryService.GetMimeType (contentType); + if (mimeType != null) { + MimeTypeNode mt = FindMimeType (mimeType); + if (mt != null) { + return mt; + } + } + } + } catch (Exception ex) { + LoggingService.LogError ("IFilePathRegistryService query failed", ex); + }
+ foreach (MimeTypeNode mt in mimeTypeNodes) { if (mt.SupportsFile (fileName)) return mt; @@ -560,6 +580,11 @@ namespace MonoDevelop.Ide.Desktop } public static bool AccessibilityInUse { get; protected set; } + + internal virtual string GetNativeRuntimeDescription () + { + return null; + } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs index 9eda1df61f..76d02a6bfa 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs @@ -25,8 +25,8 @@ // // - using System; +using System.Collections.Generic; using MonoDevelop.Projects; using MonoDevelop.Ide.CodeCompletion; using MonoDevelop.Components.Commands; @@ -191,8 +191,11 @@ namespace MonoDevelop.Ide.Editor.Extension completionTokenSrc = new CancellationTokenSource (); var caretOffset = Editor.CaretOffset; var token = completionTokenSrc.Token; + + var metadata = new Dictionary<string, string> (); + metadata ["Result"] = "Success"; try { - Counters.ProcessCodeCompletion.BeginTiming (); + Counters.ProcessCodeCompletion.BeginTiming (metadata); var task = DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (CompletionTriggerReason.CharTyped, descriptor.KeyChar), token); if (task != null) { // Show the completion window in two steps. The call to PrepareShowWindow creates the window but @@ -225,6 +228,9 @@ namespace MonoDevelop.Ide.Editor.Extension CurrentCompletionContext = null; } } finally { + if (token.IsCancellationRequested) { + metadata ["Result"] = "UserCancel"; + } Counters.ProcessCodeCompletion.EndTiming (); } }, Runtime.MainTaskScheduler); @@ -233,10 +239,13 @@ namespace MonoDevelop.Ide.Editor.Extension Counters.ProcessCodeCompletion.EndTiming (); } } catch (TaskCanceledException) { + metadata ["Result"] = "UserCancel"; Counters.ProcessCodeCompletion.EndTiming (); } catch (AggregateException) { + metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); } catch { + metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); throw; } @@ -260,8 +269,11 @@ namespace MonoDevelop.Ide.Editor.Extension completionTokenSrc = new CancellationTokenSource (); var caretOffset = Editor.CaretOffset; var token = completionTokenSrc.Token; + + var metadata = new Dictionary<string, string> (); + metadata ["Result"] = "Success"; try { - Counters.ProcessCodeCompletion.BeginTiming (); + Counters.ProcessCodeCompletion.BeginTiming (metadata); var task = DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (CompletionTriggerReason.BackspaceOrDeleteCommand, deleteOrBackspaceTriggerChar), token); if (task != null) { // Show the completion window in two steps. The call to PrepareShowWindow creates the window but @@ -299,6 +311,9 @@ namespace MonoDevelop.Ide.Editor.Extension CurrentCompletionContext = null; } } finally { + if (token.IsCancellationRequested) { + metadata ["Result"] = "UserCancel"; + } Counters.ProcessCodeCompletion.EndTiming (); } }, Runtime.MainTaskScheduler); @@ -307,11 +322,14 @@ namespace MonoDevelop.Ide.Editor.Extension } } catch (TaskCanceledException) { CurrentCompletionContext = null; + metadata ["Result"] = "UserCancel"; Counters.ProcessCodeCompletion.EndTiming (); } catch (AggregateException) { CurrentCompletionContext = null; + metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); } catch { + metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); throw; } @@ -429,8 +447,11 @@ namespace MonoDevelop.Ide.Editor.Extension CurrentCompletionContext = CompletionWidget.CreateCodeCompletionContext (cpos); CurrentCompletionContext.TriggerWordLength = wlen; + + var metadata = new Dictionary<string, string> (); + metadata ["Result"] = "Success"; try { - Counters.ProcessCodeCompletion.BeginTiming (); + Counters.ProcessCodeCompletion.BeginTiming (metadata); completionList = await DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (reason), token); if (completionList != null && completionList.TriggerWordStart >= 0) { CurrentCompletionContext.TriggerOffset = completionList.TriggerWordStart; @@ -439,6 +460,9 @@ namespace MonoDevelop.Ide.Editor.Extension if (completionList == null || !CompletionWindowManager.ShowWindow (this, (char)0, completionList, CompletionWidget, CurrentCompletionContext)) { CurrentCompletionContext = null; } + } catch (Exception) { + metadata ["Result"] = "Failure"; + throw; } finally { Counters.ProcessCodeCompletion.EndTiming (); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/HighlightUrlExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/HighlightUrlExtension.cs index 17b1e8cec4..8fc4366b7a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/HighlightUrlExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/HighlightUrlExtension.cs @@ -108,7 +108,7 @@ namespace MonoDevelop.Ide.Editor.Extension var matches = new List<(UrlType, Match, IDocumentLine)> (); var line = startLine; int o = 0; - while (line != null && line.Offset < endOffset) { + while (line != null && line.Offset <= endOffset) { if (token.IsCancellationRequested) return; string lineText = input.GetTextAt (line.Offset, line.Length); @@ -143,7 +143,7 @@ namespace MonoDevelop.Ide.Editor.Extension if (token.IsCancellationRequested) return; line = startLine; - while (line != null && line.Offset < endOffset) { + while (line != null && line.Offset <= endOffset) { foreach (var u in Editor.GetLineMarkers (line).OfType<IUrlTextLineMarker> ()) { Editor.RemoveMarker (u); markers.Remove (u); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/LineSeparatorTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/LineSeparatorTextEditorExtension.cs index 9cef24e485..6565d9fdeb 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/LineSeparatorTextEditorExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/LineSeparatorTextEditorExtension.cs @@ -31,6 +31,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Editor; +using MonoDevelop.Core; namespace MonoDevelop.Ide.Editor.Extension { @@ -84,24 +85,23 @@ namespace MonoDevelop.Ide.Editor.Extension src = new CancellationTokenSource (); if (!enabled) return; - var token = src.Token; - - var lineSeparatorService = DocumentContext?.RoslynWorkspace?.Services.GetLanguageServices (DocumentContext.AnalysisDocument.Project.Language).GetService<ILineSeparatorService> (); + var analysisDocument = DocumentContext?.AnalysisDocument; + if (analysisDocument == null) + return; + var lineSeparatorService = DocumentContext?.RoslynWorkspace?.Services.GetLanguageServices (analysisDocument.Project.Language).GetService<ILineSeparatorService> (); if (lineSeparatorService == null) return; - var separators = await lineSeparatorService.GetLineSeparatorsAsync (DocumentContext.AnalysisDocument, new TextSpan (0, Editor.Length), token); + var token = src.Token; + var separators = await lineSeparatorService.GetLineSeparatorsAsync (analysisDocument, new TextSpan (0, Editor.Length), token); if (token.IsCancellationRequested) return; - var newMarkers = new List<ITextLineMarker> (); + RemoveMarkers (); foreach (var s in separators) { var line = Editor.GetLineByOffset (s.Start); var marker = Editor.TextMarkerFactory.CreateLineSeparatorMarker (Editor); Editor.AddMarker (line, marker); - newMarkers.Add (marker); + markers.Add (marker); } - - RemoveMarkers (); - markers = newMarkers; } void RemoveMarkers () diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/EditorThemeColors.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/EditorThemeColors.cs index 2ab3563dbf..2dfdc6e619 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/EditorThemeColors.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/EditorThemeColors.cs @@ -139,22 +139,45 @@ namespace MonoDevelop.Ide.Editor.Highlighting public static readonly string UserTypesDelegates = "entity.name.delegate"; public static readonly string UserTypesMutable = "entity.name.mutable"; + public static readonly string UserField = "entity.name.field"; + [Obsolete ("Use UserField")] public static readonly string UserFieldDeclaration = "entity.name.field"; - public static readonly string UserFieldUsage = "entity.name.field.usage"; + [Obsolete ("Use UserField")] + public static readonly string UserFieldUsage = "entity.name.field"; + public static readonly string UserEnumMember = "entity.name.enummember"; + public static readonly string UserConstant = "entity.name.constant"; + + public static readonly string UserProperty = "entity.name.property"; + [Obsolete ("Use UserProperty")] public static readonly string UserPropertyDeclaration = "entity.name.property"; - public static readonly string UserPropertyUsage = "entity.name.property.usage"; + [Obsolete ("Use UserProperty")] + public static readonly string UserPropertyUsage = "entity.name.property"; + + public static readonly string UserEvent = "entity.name.event"; + [Obsolete ("Use UserEvent")] public static readonly string UserEventDeclaration = "entity.name.event"; + [Obsolete ("Use UserEvent")] public static readonly string UserEventUsage = "entity.name.event.usage"; + public static readonly string UserMethod = "entity.name.function"; + public static readonly string UserExtensionMethod = "entity.name.extensionmethod"; + [Obsolete ("Use UserMethod")] public static readonly string UserMethodDeclaration = "entity.name.function"; + [Obsolete ("Use UserMethod")] public static readonly string UserMethodUsage = "entity.name.function.usage"; + public static readonly string UserParameter = "entity.name.parameter"; + [Obsolete ("Use UserParameter")] public static readonly string UserParameterDeclaration = "entity.name.parameter"; + [Obsolete ("Use UserParameter")] public static readonly string UserParameterUsage = "entity.name.parameter.usage"; + public static readonly string UserLocal = "entity.name.local"; + [Obsolete ("Use UserLocal")] public static readonly string UserVariableDeclaration = "entity.name.local"; + [Obsolete ("Use UserLocal")] public static readonly string UserVariableUsage = "entity.name.local.usage"; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/Formats/OldFormat.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/Formats/OldFormat.cs index 09675a6a12..91ddd3b9db 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/Formats/OldFormat.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/Formats/OldFormat.cs @@ -33,6 +33,7 @@ using System.Xml.XPath; using System.Reflection; using System.Text; using System.Xml; +using MonoDevelop.Core; using MonoDevelop.Components; using MonoDevelop.Core.Text; using System.Collections.Immutable; @@ -77,7 +78,7 @@ namespace MonoDevelop.Ide.Editor.Highlighting }; if (style.FontStyle != Xwt.Drawing.FontStyle.Normal || style.FontWeight != Xwt.Drawing.FontWeight.Normal) { - var fontStyle = new StringBuilder (); + var fontStyle = StringBuilderCache.Allocate (); if (style.FontStyle != Xwt.Drawing.FontStyle.Normal) { fontStyle.Append (style.FontStyle.ToString ().ToLower ()); fontStyle.Append (" "); @@ -85,7 +86,7 @@ namespace MonoDevelop.Ide.Editor.Highlighting if (style.FontWeight != Xwt.Drawing.FontWeight.Normal) { fontStyle.Append (style.FontWeight.ToString ().ToLower ()); } - result ["fontStyle"] = fontStyle.ToString (); + result ["fontStyle"] = StringBuilderCache.ReturnAndFree (fontStyle); } return result; } @@ -258,18 +259,16 @@ namespace MonoDevelop.Ide.Editor.Highlighting settings.Add (new ThemeSetting ("User Types(Delegates)", new List<string> { EditorThemeColors.UserTypesDelegates }, ConvertChunkStyle (colorScheme.UserTypesDelegates))); settings.Add (new ThemeSetting ("User Types(Mutable)", new List<string> { EditorThemeColors.UserTypesMutable }, ConvertChunkStyle (colorScheme.UserTypesMutable))); - settings.Add (new ThemeSetting ("User Field(Declaration)", new List<string> { EditorThemeColors.UserFieldDeclaration }, ConvertChunkStyle (colorScheme.UserFieldDeclaration))); - settings.Add (new ThemeSetting ("User Field(Usage)", new List<string> { EditorThemeColors.UserFieldUsage }, ConvertChunkStyle (colorScheme.UserFieldUsage))); - settings.Add (new ThemeSetting ("User Property(Declaration)", new List<string> { EditorThemeColors.UserPropertyDeclaration }, ConvertChunkStyle (colorScheme.UserPropertyDeclaration))); - settings.Add (new ThemeSetting ("User Property(Usage)", new List<string> { EditorThemeColors.UserPropertyUsage }, ConvertChunkStyle (colorScheme.UserPropertyUsage))); - settings.Add (new ThemeSetting ("User Event(Declaration)", new List<string> { EditorThemeColors.UserEventDeclaration }, ConvertChunkStyle (colorScheme.UserEventDeclaration))); - settings.Add (new ThemeSetting ("User Event(Usage)", new List<string> { EditorThemeColors.UserEventUsage }, ConvertChunkStyle (colorScheme.UserEventUsage))); - settings.Add (new ThemeSetting ("User Method(Declaration)", new List<string> { EditorThemeColors.UserMethodDeclaration }, ConvertChunkStyle (colorScheme.UserMethodDeclaration))); - settings.Add (new ThemeSetting ("User Method(Usage)", new List<string> { EditorThemeColors.UserMethodUsage }, ConvertChunkStyle (colorScheme.UserMethodUsage))); - settings.Add (new ThemeSetting ("User Parameter(Declaration)", new List<string> { EditorThemeColors.UserParameterDeclaration }, ConvertChunkStyle (colorScheme.UserParameterDeclaration))); - settings.Add (new ThemeSetting ("User Parameter(Usage)", new List<string> { EditorThemeColors.UserParameterUsage }, ConvertChunkStyle (colorScheme.UserParameterUsage))); - settings.Add (new ThemeSetting ("User Variable(Declaration)", new List<string> { EditorThemeColors.UserVariableDeclaration }, ConvertChunkStyle (colorScheme.UserVariableDeclaration))); - settings.Add (new ThemeSetting ("User Variable(Usage)", new List<string> { EditorThemeColors.UserVariableUsage }, ConvertChunkStyle (colorScheme.UserVariableUsage))); + settings.Add (new ThemeSetting ("User Field", new List<string> { EditorThemeColors.UserField }, ConvertChunkStyle (colorScheme.UserFieldDeclaration))); + settings.Add (new ThemeSetting ("User Enum Member", new List<string> { EditorThemeColors.UserField }, ConvertChunkStyle (colorScheme.UserFieldDeclaration))); + settings.Add (new ThemeSetting ("User Constant", new List<string> { EditorThemeColors.UserConstant }, ConvertChunkStyle (colorScheme.UserFieldDeclaration))); + + settings.Add (new ThemeSetting ("User Property", new List<string> { EditorThemeColors.UserProperty }, ConvertChunkStyle (colorScheme.UserPropertyDeclaration))); + settings.Add (new ThemeSetting ("User Event", new List<string> { EditorThemeColors.UserEvent }, ConvertChunkStyle (colorScheme.UserEventDeclaration))); + settings.Add (new ThemeSetting ("User Method", new List<string> { EditorThemeColors.UserMethod }, ConvertChunkStyle (colorScheme.UserMethodDeclaration))); + settings.Add (new ThemeSetting ("User Extension Method", new List<string> { EditorThemeColors.UserExtensionMethod }, ConvertChunkStyle (colorScheme.UserMethodDeclaration))); + settings.Add (new ThemeSetting ("User Parameter", new List<string> { EditorThemeColors.UserParameter }, ConvertChunkStyle (colorScheme.UserParameterDeclaration))); + settings.Add (new ThemeSetting ("User Variable", new List<string> { EditorThemeColors.UserLocal }, ConvertChunkStyle (colorScheme.UserVariableDeclaration))); settings.Add (new ThemeSetting ("CSS Comment", new List<string> { "comment.block.css" }, ConvertChunkStyle (colorScheme.CssComment))); settings.Add (new ThemeSetting ("CSS Keyword", new List<string> { "keyword.other.css" }, ConvertChunkStyle (colorScheme.CssKeyword))); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ISyntaxHighlighting.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ISyntaxHighlighting.cs index 34b6f7d4f3..215e49ba47 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ISyntaxHighlighting.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/ISyntaxHighlighting.cs @@ -31,6 +31,7 @@ using MonoDevelop.Core.Text; using System.Collections.Immutable; using System.Threading.Tasks; using System.Threading; +using System.Linq; namespace MonoDevelop.Ide.Editor.Highlighting { @@ -41,6 +42,20 @@ namespace MonoDevelop.Ide.Editor.Highlighting /// The segment offsets are 0 at line start regardless of where the line is inside the document. /// </summary> public IReadOnlyList<ColoredSegment> Segments { get; private set; } + + bool? isContinuedBeyondLineEnd; + public bool? IsContinuedBeyondLineEnd { + get { + if (isContinuedBeyondLineEnd.HasValue) + return isContinuedBeyondLineEnd.Value; + var result = Segments.Count > 0 ? TextSegment.Length < Segments.Last ().EndOffset : false; + return isContinuedBeyondLineEnd = result; + } + set { + isContinuedBeyondLineEnd = value; + } + } + public HighlightedLine (ISegment textSegment, IReadOnlyList<ColoredSegment> segments) { TextSegment = textSegment; @@ -89,4 +104,4 @@ namespace MonoDevelop.Ide.Editor.Highlighting {
} } -}
\ No newline at end of file +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/PObject.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/PObject.cs index 865191b39d..b4616c511c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/PObject.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/PObject.cs @@ -887,13 +887,13 @@ namespace MonoDevelop.Ide.Editor.Highlighting public string ToStringList () { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); foreach (PString str in list.OfType<PString> ()) { if (sb.Length > 0) sb.Append (", "); sb.Append (str); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } public IEnumerator<PObject> GetEnumerator () diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/RoslynClassificationHighlighting.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/RoslynClassificationHighlighting.cs index 15c2b91a0a..6c3b47eb9d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/RoslynClassificationHighlighting.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/RoslynClassificationHighlighting.cs @@ -58,56 +58,79 @@ namespace MonoDevelop.Ide.Editor.Highlighting this.defaultScope = new ScopeStack (defaultScope); this.userScope = this.defaultScope.Push (EditorThemeColors.UserTypes); - classificationMap = new Dictionary<string, ScopeStack> { - [ClassificationTypeNames.Comment] = MakeScope ("comment." + defaultScope), - [ClassificationTypeNames.ExcludedCode] = MakeScope ("comment.excluded." + defaultScope), - [ClassificationTypeNames.Identifier] = MakeScope (defaultScope), - [ClassificationTypeNames.Keyword] = MakeScope ("keyword." + defaultScope), - [ClassificationTypeNames.NumericLiteral] = MakeScope ("constant.numeric." + defaultScope), - [ClassificationTypeNames.Operator] = MakeScope (defaultScope), - [ClassificationTypeNames.PreprocessorKeyword] = MakeScope ("meta.preprocessor." + defaultScope), - [ClassificationTypeNames.StringLiteral] = MakeScope ("string." + defaultScope), - [ClassificationTypeNames.WhiteSpace] = MakeScope ("text." + defaultScope), - [ClassificationTypeNames.Text] = MakeScope ("text." + defaultScope), - - [ClassificationTypeNames.PreprocessorText] = MakeScope ("meta.preprocessor.region.name." + defaultScope), - [ClassificationTypeNames.Punctuation] = MakeScope ("punctuation." + defaultScope), - [ClassificationTypeNames.VerbatimStringLiteral] = MakeScope ("string.verbatim." + defaultScope), - - [ClassificationTypeNames.ClassName] = MakeScope ("entity.name.class." + defaultScope), - [ClassificationTypeNames.DelegateName] = MakeScope ("entity.name.delegate." + defaultScope), - [ClassificationTypeNames.EnumName] = MakeScope ("entity.name.enum." + defaultScope), - [ClassificationTypeNames.InterfaceName] = MakeScope ("entity.name.interface." + defaultScope), - [ClassificationTypeNames.ModuleName] = MakeScope ("entity.name.module." + defaultScope), - [ClassificationTypeNames.StructName] = MakeScope ("entity.name.struct." + defaultScope), - [ClassificationTypeNames.TypeParameterName] = MakeScope ("entity.name.typeparameter." + defaultScope), - - [ClassificationTypeNames.XmlDocCommentAttributeName] = MakeScope ("comment.line.documentation." + defaultScope), - [ClassificationTypeNames.XmlDocCommentAttributeQuotes] = MakeScope ("comment.line.documentation." + defaultScope), - [ClassificationTypeNames.XmlDocCommentAttributeValue] = MakeScope ("comment.line.documentation." + defaultScope), - [ClassificationTypeNames.XmlDocCommentCDataSection] = MakeScope ("comment.line.documentation." + defaultScope), - [ClassificationTypeNames.XmlDocCommentComment] = MakeScope ("comment.line.documentation." + defaultScope), - [ClassificationTypeNames.XmlDocCommentDelimiter] = MakeScope ("comment.line.documentation." + defaultScope), - [ClassificationTypeNames.XmlDocCommentEntityReference] = MakeScope ("comment.line.documentation." + defaultScope), - [ClassificationTypeNames.XmlDocCommentName] = MakeScope ("comment.line.documentation." + defaultScope), - [ClassificationTypeNames.XmlDocCommentProcessingInstruction] = MakeScope ("comment.line.documentation." + defaultScope), - [ClassificationTypeNames.XmlDocCommentText] = MakeScope ("comment.line.documentation." + defaultScope), - - [ClassificationTypeNames.XmlLiteralAttributeName] = MakeScope ("entity.other.attribute-name." + defaultScope), - [ClassificationTypeNames.XmlLiteralAttributeQuotes] = MakeScope ("punctuation.definition.string." + defaultScope), - [ClassificationTypeNames.XmlLiteralAttributeValue] = MakeScope ("string.quoted." + defaultScope), - [ClassificationTypeNames.XmlLiteralCDataSection] = MakeScope ("text." + defaultScope), - [ClassificationTypeNames.XmlLiteralComment] = MakeScope ("comment.block." + defaultScope), - [ClassificationTypeNames.XmlLiteralDelimiter] = MakeScope (defaultScope), - [ClassificationTypeNames.XmlLiteralEmbeddedExpression] = MakeScope (defaultScope), - [ClassificationTypeNames.XmlLiteralEntityReference] = MakeScope (defaultScope), - [ClassificationTypeNames.XmlLiteralName] = MakeScope ("entity.name.tag.localname." + defaultScope), - [ClassificationTypeNames.XmlLiteralProcessingInstruction] = MakeScope (defaultScope), - [ClassificationTypeNames.XmlLiteralText] = MakeScope ("text." + defaultScope), + classificationMap = GetClassificationMap (defaultScope); + } + static ImmutableDictionary<string, Dictionary<string, ScopeStack>> classificationMapCache = ImmutableDictionary<string, Dictionary<string, ScopeStack>>.Empty; + + public static Dictionary<string, ScopeStack> GetClassificationMap (string scope) + { + Dictionary<string, ScopeStack> result; + if (classificationMapCache.TryGetValue (scope, out result)) + return result; + var defaultScopeStack = new ScopeStack (scope); + result = new Dictionary<string, ScopeStack> { + [ClassificationTypeNames.Comment] = MakeScope (defaultScopeStack, "comment." + scope), + [ClassificationTypeNames.ExcludedCode] = MakeScope (defaultScopeStack, "comment.excluded." + scope), + [ClassificationTypeNames.Identifier] = MakeScope (defaultScopeStack, scope), + [ClassificationTypeNames.Keyword] = MakeScope (defaultScopeStack, "keyword." + scope), + [ClassificationTypeNames.NumericLiteral] = MakeScope (defaultScopeStack, "constant.numeric." + scope), + [ClassificationTypeNames.Operator] = MakeScope (defaultScopeStack, scope), + [ClassificationTypeNames.PreprocessorKeyword] = MakeScope (defaultScopeStack, "meta.preprocessor." + scope), + [ClassificationTypeNames.StringLiteral] = MakeScope (defaultScopeStack, "string." + scope), + [ClassificationTypeNames.WhiteSpace] = MakeScope (defaultScopeStack, "text." + scope), + [ClassificationTypeNames.Text] = MakeScope (defaultScopeStack, "text." + scope), + + [ClassificationTypeNames.PreprocessorText] = MakeScope (defaultScopeStack, "meta.preprocessor.region.name." + scope), + [ClassificationTypeNames.Punctuation] = MakeScope (defaultScopeStack, "punctuation." + scope), + [ClassificationTypeNames.VerbatimStringLiteral] = MakeScope (defaultScopeStack, "string.verbatim." + scope), + + [ClassificationTypeNames.ClassName] = MakeScope (defaultScopeStack, "entity.name.class." + scope), + [ClassificationTypeNames.DelegateName] = MakeScope (defaultScopeStack, "entity.name.delegate." + scope), + [ClassificationTypeNames.EnumName] = MakeScope (defaultScopeStack, "entity.name.enum." + scope), + [ClassificationTypeNames.InterfaceName] = MakeScope (defaultScopeStack, "entity.name.interface." + scope), + [ClassificationTypeNames.ModuleName] = MakeScope (defaultScopeStack, "entity.name.module." + scope), + [ClassificationTypeNames.StructName] = MakeScope (defaultScopeStack, "entity.name.struct." + scope), + [ClassificationTypeNames.TypeParameterName] = MakeScope (defaultScopeStack, "entity.name.typeparameter." + scope), + + [ClassificationTypeNames.FieldName] = MakeScope (defaultScopeStack, "entity.name.field." + scope), + [ClassificationTypeNames.EnumMemberName] = MakeScope (defaultScopeStack, "entity.name.enummember." + scope), + [ClassificationTypeNames.ConstantName] = MakeScope (defaultScopeStack, "entity.name.constant." + scope), + [ClassificationTypeNames.LocalName] = MakeScope (defaultScopeStack, "entity.name.local." + scope), + [ClassificationTypeNames.ParameterName] = MakeScope (defaultScopeStack, "entity.name.parameter." + scope), + [ClassificationTypeNames.ExtensionMethodName] = MakeScope (defaultScopeStack, "entity.name.extensionmethod." + scope), + [ClassificationTypeNames.MethodName] = MakeScope (defaultScopeStack, "entity.name.function." + scope), + [ClassificationTypeNames.PropertyName] = MakeScope (defaultScopeStack, "entity.name.property." + scope), + [ClassificationTypeNames.EventName] = MakeScope (defaultScopeStack, "entity.name.event." + scope), + + [ClassificationTypeNames.XmlDocCommentAttributeName] = MakeScope (defaultScopeStack, "comment.line.documentation." + scope), + [ClassificationTypeNames.XmlDocCommentAttributeQuotes] = MakeScope (defaultScopeStack, "comment.line.documentation." + scope), + [ClassificationTypeNames.XmlDocCommentAttributeValue] = MakeScope (defaultScopeStack, "comment.line.documentation." + scope), + [ClassificationTypeNames.XmlDocCommentCDataSection] = MakeScope (defaultScopeStack, "comment.line.documentation." + scope), + [ClassificationTypeNames.XmlDocCommentComment] = MakeScope (defaultScopeStack, "comment.line.documentation." + scope), + [ClassificationTypeNames.XmlDocCommentDelimiter] = MakeScope (defaultScopeStack, "comment.line.documentation." + scope), + [ClassificationTypeNames.XmlDocCommentEntityReference] = MakeScope (defaultScopeStack, "comment.line.documentation." + scope), + [ClassificationTypeNames.XmlDocCommentName] = MakeScope (defaultScopeStack, "comment.line.documentation." + scope), + [ClassificationTypeNames.XmlDocCommentProcessingInstruction] = MakeScope (defaultScopeStack, "comment.line.documentation." + scope), + [ClassificationTypeNames.XmlDocCommentText] = MakeScope (defaultScopeStack, "comment.line.documentation." + scope), + + [ClassificationTypeNames.XmlLiteralAttributeName] = MakeScope (defaultScopeStack, "entity.other.attribute-name." + scope), + [ClassificationTypeNames.XmlLiteralAttributeQuotes] = MakeScope (defaultScopeStack, "punctuation.definition.string." + scope), + [ClassificationTypeNames.XmlLiteralAttributeValue] = MakeScope (defaultScopeStack, "string.quoted." + scope), + [ClassificationTypeNames.XmlLiteralCDataSection] = MakeScope (defaultScopeStack, "text." + scope), + [ClassificationTypeNames.XmlLiteralComment] = MakeScope (defaultScopeStack, "comment.block." + scope), + [ClassificationTypeNames.XmlLiteralDelimiter] = MakeScope (defaultScopeStack, scope), + [ClassificationTypeNames.XmlLiteralEmbeddedExpression] = MakeScope (defaultScopeStack, scope), + [ClassificationTypeNames.XmlLiteralEntityReference] = MakeScope (defaultScopeStack, scope), + [ClassificationTypeNames.XmlLiteralName] = MakeScope (defaultScopeStack, "entity.name.tag.localname." + scope), + [ClassificationTypeNames.XmlLiteralProcessingInstruction] = MakeScope (defaultScopeStack, scope), + [ClassificationTypeNames.XmlLiteralText] = MakeScope (defaultScopeStack, "text." + scope), }; + classificationMapCache = classificationMapCache.SetItem (scope, result); + + return result; } - ScopeStack MakeScope (string scope) + static ScopeStack MakeScope (ScopeStack defaultScope, string scope) { return defaultScope.Push (scope); } @@ -140,14 +163,18 @@ namespace MonoDevelop.Ide.Editor.Highlighting ScopeStack scopeStack; foreach (var curSpan in classifications) { - if (curSpan.TextSpan.Start > lastClassifiedOffsetEnd) { + var start = Math.Max (offset, curSpan.TextSpan.Start); + if (start < lastClassifiedOffsetEnd) { // Work around for : https://github.com/dotnet/roslyn/issues/25648 + continue; + } + if (start > lastClassifiedOffsetEnd) { scopeStack = userScope; - ColoredSegment whitespaceSegment = new ColoredSegment (lastClassifiedOffsetEnd - offset, curSpan.TextSpan.Start - lastClassifiedOffsetEnd, scopeStack); + ColoredSegment whitespaceSegment = new ColoredSegment (lastClassifiedOffsetEnd - offset, start - lastClassifiedOffsetEnd, scopeStack); coloredSegments.Add (whitespaceSegment); } scopeStack = GetStyleScopeStackFromClassificationType (curSpan.ClassificationType); - ColoredSegment curColoredSegment = new ColoredSegment (curSpan.TextSpan.Start - offset, curSpan.TextSpan.Length, scopeStack); + ColoredSegment curColoredSegment = new ColoredSegment (start - offset, curSpan.TextSpan.Length, scopeStack); coloredSegments.Add (curColoredSegment); lastClassifiedOffsetEnd = curSpan.TextSpan.End; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/StackMatchExpression.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/StackMatchExpression.cs index 1e5ce98dfc..6113e45a1d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/StackMatchExpression.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/StackMatchExpression.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Text; using Roslyn.Utilities; +using MonoDevelop.Core; namespace MonoDevelop.Ide.Editor.Highlighting { @@ -38,7 +39,7 @@ namespace MonoDevelop.Ide.Editor.Highlighting public static StackMatchExpression Parse (string expression) { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); var stackStack = new Stack<Stack<StackMatchExpression>> (); var exprStack = new Stack<StackMatchExpression> (); @@ -88,6 +89,7 @@ namespace MonoDevelop.Ide.Editor.Highlighting } if (sb.Length > 0) exprStack.Push (CreateMatchExpression (sb)); + StringBuilderCache.Free (sb); ShrinkStack (exprStack); if (exprStack.IsEmpty ()) return new StringMatchExpression (""); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlighting.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlighting.cs index c8340e77e8..183d0e01a8 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlighting.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlighting.cs @@ -10,6 +10,7 @@ using System.IO.Compression; using System.Threading.Tasks; using System.Threading; using MonoDevelop.Core.Text; +using Microsoft.CodeAnalysis.Execution; namespace MonoDevelop.Ide.Editor.Highlighting { @@ -188,7 +189,7 @@ namespace MonoDevelop.Ide.Editor.Highlighting int lastMatch = -1; var highlightedSegment = new TextSegment (startOffset, length); string lineText = text.GetTextAt (startOffset, length); - + var initialState = state.Clone (); int timeoutOccursAt; unchecked { timeoutOccursAt = Environment.TickCount + (int)matchTimeout.TotalMilliseconds; @@ -205,8 +206,6 @@ namespace MonoDevelop.Ide.Editor.Highlighting lastContexts.Clear (); lastContexts.Add (currentContext); } - if (length <= 0) - goto end; lastMatch = offset; currentContext = ContextStack.Peek (); match = null; @@ -218,7 +217,12 @@ namespace MonoDevelop.Ide.Editor.Highlighting if (r == null) continue; try { - var possibleMatch = r.Match (lineText, offset, length, matchTimeout); + Match possibleMatch; + if (r.pattern == "(?<=\\})" && offset > 0) { // HACK to fix typescript highlighting. + possibleMatch = r.Match (lineText, offset - 1, length, matchTimeout); + } else { + possibleMatch = r.Match (lineText, offset, length, matchTimeout); + } if (possibleMatch.Success) { if (match == null || possibleMatch.Index < match.Index) { match = possibleMatch; @@ -236,6 +240,9 @@ namespace MonoDevelop.Ide.Editor.Highlighting continue; } } + if (length <= 0 && curMatch == null) + goto end; + if (Environment.TickCount >= timeoutOccursAt) { curMatch.GotTimeout = true; goto end; @@ -330,7 +337,9 @@ namespace MonoDevelop.Ide.Editor.Highlighting segments.Add (new ColoredSegment (curSegmentOffset, endOffset - curSegmentOffset, ScopeStack)); } - return Task.FromResult (new HighlightedLine (highlightedSegment, segments)); + return Task.FromResult (new HighlightedLine (highlightedSegment, segments) { + IsContinuedBeyondLineEnd = !initialState.Equals (state) + }); } void PushStack (SyntaxMatch curMatch, IEnumerable<SyntaxContext> nextContexts) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/themes/FallbackStyle.json b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/themes/FallbackStyle.json index e53bff7df6..8a779a0171 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/themes/FallbackStyle.json +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/themes/FallbackStyle.json @@ -29,7 +29,7 @@ "colors": [ { "name": "Background(Read Only)", "color": "white" }, - { "name": "Search result background", "color": "#fffeb7" }, + { "name": "Search result background", "color": "#fcff54" }, { "name": "Search result background (highlighted)", "color": "#fffc38" }, { "name": "Column Ruler", "color": "#eeeeee" }, diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/themes/LightStyle.json b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/themes/LightStyle.json index fbbbb86040..5e6891a78d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/themes/LightStyle.json +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/themes/LightStyle.json @@ -29,7 +29,7 @@ "colors": [ { "name": "Background(Read Only)", "color": "white" }, - { "name": "Search result background", "color": "#fffeb7" }, + { "name": "Search result background", "color": "#fcff54" }, { "name": "Search result background (highlighted)", "color": "#fffc38" }, { "name": "Column Ruler", "color": "#eeeeee" }, diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.TextMate/TextMateCompletionTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.TextMate/TextMateCompletionTextEditorExtension.cs index fb5cca77ca..cd7c7da16d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.TextMate/TextMateCompletionTextEditorExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.TextMate/TextMateCompletionTextEditorExtension.cs @@ -33,6 +33,7 @@ using System.Collections.Generic; using System.Text; using MonoDevelop.Core.Text; using MonoDevelop.Ide.TypeSystem; +using MonoDevelop.Core; namespace MonoDevelop.Ide.Editor.TextMate { @@ -85,7 +86,7 @@ namespace MonoDevelop.Ide.Editor.TextMate { return Task.Run (delegate { var result = new HashSet<string> (); - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); int i = 0; while (i < source.Length) { char ch = source[i]; @@ -110,6 +111,7 @@ namespace MonoDevelop.Ide.Editor.TextMate i++; } + StringBuilderCache.Free (sb); return (IEnumerable<string>)result; }); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/AutoSave.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/AutoSave.cs index ca8fba7763..5d5adc3915 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/AutoSave.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/AutoSave.cs @@ -70,11 +70,11 @@ namespace MonoDevelop.Ide.Editor static MD5 md5 = MD5.Create (); static string GetMD5 (string data) { - var result = new StringBuilder(); + var result = StringBuilderCache.Allocate(); foreach (var b in md5.ComputeHash (Encoding.ASCII.GetBytes (data))) { result.Append(b.ToString("X2")); } - return result.ToString(); + return StringBuilderCache.ReturnAndFree (result); } /// <summary> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs index 3d691ea7d4..5ad1830664 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/DefaultSourceEditorOptions.cs @@ -28,6 +28,8 @@ using MonoDevelop.Core; using MonoDevelop.Ide.Gui.Content; using MonoDevelop.Ide.Fonts; using MonoDevelop.Ide.Editor.Extension; +using Microsoft.VisualStudio.CodingConventions; +using System.Threading.Tasks; namespace MonoDevelop.Ide.Editor { @@ -52,6 +54,7 @@ namespace MonoDevelop.Ide.Editor static DefaultSourceEditorOptions instance; //static TextStylePolicy defaultPolicy; static bool inited; + ICodingConventionContext context; public static DefaultSourceEditorOptions Instance { get { return instance; } @@ -278,7 +281,7 @@ namespace MonoDevelop.Ide.Editor removeTrailingWhitespaces = currentPolicy.RemoveTrailingWhitespace; //PropertyService.Get ("RemoveTrailingWhitespaces", true); } - public ITextEditorOptions WithTextStyle (MonoDevelop.Ide.Gui.Content.TextStylePolicy policy) + public DefaultSourceEditorOptions WithTextStyle (MonoDevelop.Ide.Gui.Content.TextStylePolicy policy) { if (policy == null) throw new ArgumentNullException ("policy"); @@ -288,6 +291,52 @@ namespace MonoDevelop.Ide.Editor return result; } + internal void SetContext (ICodingConventionContext context) + { + this.context = context; + context.CodingConventionsChangedAsync += UpdateContextOptions; + UpdateContextOptions (null, null); + } + + private Task UpdateContextOptions (object sender, CodingConventionsChangedEventArgs arg) + { + if (context == null) + return Task.FromResult (false); + + defaultEolMarkerFromContext = null; + if (context.CurrentConventions.UniversalConventions.TryGetLineEnding (out string eolMarker)) + defaultEolMarkerFromContext = eolMarker; + + tabsToSpacesFromContext = null; + if (context.CurrentConventions.UniversalConventions.TryGetIndentStyle (out Microsoft.VisualStudio.CodingConventions.IndentStyle result)) + tabsToSpacesFromContext = result == Microsoft.VisualStudio.CodingConventions.IndentStyle.Spaces; + + indentationSizeFromContext = null; + if (context.CurrentConventions.UniversalConventions.TryGetIndentSize (out int indentSize)) + indentationSizeFromContext = indentSize; + + removeTrailingWhitespacesFromContext = null; + if (context.CurrentConventions.UniversalConventions.TryGetAllowTrailingWhitespace (out bool allowTrailing)) + removeTrailingWhitespacesFromContext = !allowTrailing; + + tabSizeFromContext = null; + if (context.CurrentConventions.UniversalConventions.TryGetTabWidth (out int tSize)) + tabSizeFromContext = tSize; + + rulerColumnFromContext = null; + showRulerFromContext = null; + if (context.CurrentConventions.TryGetConventionValue<string> (EditorConfigService.MaxLineLengthConvention, out string maxLineLength)) { + if (maxLineLength != "off" && int.TryParse (maxLineLength, out int i)) { + rulerColumnFromContext = i; + showRulerFromContext = true; + } else { + showRulerFromContext = false; + } + } + + return Task.FromResult (true); + } + #region new options public bool EnableAutoCodeCompletion { @@ -420,9 +469,11 @@ namespace MonoDevelop.Ide.Editor #region ITextEditorOptions string defaultEolMarker = Environment.NewLine; + string defaultEolMarkerFromContext = null; + public string DefaultEolMarker { get { - return defaultEolMarker; + return defaultEolMarkerFromContext ?? defaultEolMarker; } set { if (defaultEolMarker != value) { @@ -472,9 +523,10 @@ namespace MonoDevelop.Ide.Editor } bool tabsToSpaces = true; + bool? tabsToSpacesFromContext; public bool TabsToSpaces { get { - return tabsToSpaces; + return tabsToSpacesFromContext ?? tabsToSpaces; } set { if (tabsToSpaces != value) { @@ -486,9 +538,10 @@ namespace MonoDevelop.Ide.Editor } int indentationSize = 4; + int? indentationSizeFromContext; public int IndentationSize { get { - return indentationSize; + return indentationSizeFromContext ?? indentationSize; } set { if (indentationSize != value) { @@ -505,21 +558,23 @@ namespace MonoDevelop.Ide.Editor return TabsToSpaces ? new string (' ', this.TabSize) : "\t"; } } - + + int? tabSizeFromContext; public int TabSize { get { - return IndentationSize; + return tabSizeFromContext ?? IndentationSize; } set { IndentationSize = value; } } - bool removeTrailingWhitespaces = true; + bool? removeTrailingWhitespacesFromContext; + public bool RemoveTrailingWhitespaces { get { - return removeTrailingWhitespaces; + return removeTrailingWhitespacesFromContext ?? removeTrailingWhitespaces; } set { if (removeTrailingWhitespaces != value) { @@ -600,10 +655,12 @@ namespace MonoDevelop.Ide.Editor } int rulerColumn = 120; + int? rulerColumnFromContext; + public int RulerColumn { get { - return rulerColumn; + return rulerColumnFromContext ?? rulerColumn; } set { if (rulerColumn != value) { @@ -615,9 +672,10 @@ namespace MonoDevelop.Ide.Editor } ConfigurationProperty<bool> showRuler = ConfigurationProperty.Create ("ShowRuler", true); + bool? showRulerFromContext; public bool ShowRuler { get { - return showRuler; + return showRulerFromContext ?? showRuler; } set { if (showRuler.Set (value)) @@ -763,6 +821,7 @@ namespace MonoDevelop.Ide.Editor public bool SmartBackspace{ get { return smartBackspace; + } set { if (smartBackspace.Set (value)) @@ -775,6 +834,8 @@ namespace MonoDevelop.Ide.Editor { FontService.RemoveCallback (UpdateFont); IdeApp.Preferences.ColorScheme.Changed -= OnColorSchemeChanged; + if (context != null) + context.CodingConventionsChangedAsync -= UpdateContextOptions; } protected void OnChanged (EventArgs args) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs index 9fdeca3319..ea6fe11b15 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditActions.cs @@ -37,129 +37,129 @@ namespace MonoDevelop.Ide.Editor { public static void MoveCaretDown (TextEditor editor) { - editor.EditorActionHost.MoveCaretDown (); + editor.EditorOperations.MoveLineDown (false); } public static void MoveCaretUp (TextEditor editor) { - editor.EditorActionHost.MoveCaretUp (); + editor.EditorOperations.MoveLineUp (false); } public static void MoveCaretRight (TextEditor editor) { - editor.EditorActionHost.MoveCaretRight (); + editor.EditorOperations.MoveToPreviousCharacter (false); } public static void MoveCaretLeft (TextEditor editor) { - editor.EditorActionHost.MoveCaretLeft (); + editor.EditorOperations.MoveToNextCharacter (false); } public static void MoveCaretToLineEnd (TextEditor editor) { - editor.EditorActionHost.MoveCaretToLineEnd (); + editor.EditorOperations.MoveToEndOfLine (false); } public static void MoveCaretToLineStart (TextEditor editor) { - editor.EditorActionHost.MoveCaretToLineStart (); + editor.EditorOperations.MoveToStartOfLine (false); } public static void MoveCaretToDocumentStart (TextEditor editor) { - editor.EditorActionHost.MoveCaretToDocumentStart (); + editor.EditorOperations.MoveToStartOfDocument (false); } public static void MoveCaretToDocumentEnd (TextEditor editor) { - editor.EditorActionHost.MoveCaretToDocumentEnd (); + editor.EditorOperations.MoveToEndOfDocument (false); } public static void Backspace (TextEditor editor) { - editor.EditorActionHost.Backspace (); + editor.EditorOperations.Backspace (); } public static void Delete (TextEditor editor) { - editor.EditorActionHost.Delete (); + editor.EditorOperations.Delete (); } public static void ClipboardCopy (TextEditor editor) { - editor.EditorActionHost.ClipboardCopy (); + editor.EditorOperations.CopySelection (); } public static void ClipboardCut (TextEditor editor) { - editor.EditorActionHost.ClipboardCut (); + editor.EditorOperations.CutSelection (); } public static void ClipboardPaste (TextEditor editor) { - editor.EditorActionHost.ClipboardPaste (); + editor.EditorOperations.Paste (); } public static void SelectAll (TextEditor editor) { - editor.EditorActionHost.SelectAll (); + editor.EditorOperations.SelectAll (); } public static void NewLine (TextEditor editor) { - editor.EditorActionHost.NewLine (); + editor.EditorOperations.InsertNewLine (); } public static void PageUp (TextEditor textEditor) { - textEditor.EditorActionHost.PageUp (); + textEditor.EditorOperations.PageUp (false); } public static void PageDown (TextEditor textEditor) { - textEditor.EditorActionHost.PageDown (); + textEditor.EditorOperations.PageDown (false); } public static void Undo (TextEditor editor) { - editor.EditorActionHost.Undo (); + ((IMonoDevelopEditorOperations)editor.EditorOperations).Undo (); } public static void Redo (TextEditor editor) { - editor.EditorActionHost.Redo (); + ((IMonoDevelopEditorOperations)editor.EditorOperations).Redo (); } public static void DeleteCurrentLine (TextEditor textEditor) { - textEditor.EditorActionHost.DeleteCurrentLine (); + textEditor.EditorOperations.DeleteFullLine (); } public static void DeleteCurrentLineToEnd (TextEditor textEditor) { - textEditor.EditorActionHost.DeleteCurrentLineToEnd (); + textEditor.EditorOperations.DeleteToEndOfLine (); } public static void ScrollLineUp (TextEditor textEditor) { - textEditor.EditorActionHost.ScrollLineUp (); + textEditor.EditorOperations.ScrollLineTop (); } public static void ScrollLineDown (TextEditor textEditor) { - textEditor.EditorActionHost.ScrollLineDown (); + textEditor.EditorOperations.ScrollLineBottom (); } public static void ScrollPageUp (TextEditor textEditor) { - textEditor.EditorActionHost.ScrollPageUp (); + textEditor.EditorOperations.ScrollPageUp (); } public static void ScrollPageDown (TextEditor textEditor) { - textEditor.EditorActionHost.ScrollPageDown (); + textEditor.EditorOperations.ScrollPageDown (); } public static void GotoMatchingBrace (TextEditor textEditor) @@ -171,27 +171,27 @@ namespace MonoDevelop.Ide.Editor public static void MovePrevWord (TextEditor textEditor) { - textEditor.EditorActionHost.MovePrevWord (); + textEditor.EditorOperations.MoveToPreviousWord (false); } public static void MoveNextWord (TextEditor textEditor) { - textEditor.EditorActionHost.MoveNextWord (); + textEditor.EditorOperations.MoveToNextWord (false); } public static void MovePrevSubWord (TextEditor textEditor) { - textEditor.EditorActionHost.MovePrevSubWord (); + ((IMonoDevelopEditorOperations)textEditor.EditorOperations).MoveToPrevSubWord (); } public static void MoveNextSubWord (TextEditor textEditor) { - textEditor.EditorActionHost.MoveNextSubWord (); + ((IMonoDevelopEditorOperations)textEditor.EditorOperations).MoveToNextSubWord (); } public static void ShowQuickInfo (TextEditor textEditor) { - textEditor.EditorActionHost.ShowQuickInfo (); + ((IMonoDevelopEditorOperations)textEditor.EditorOperations).ShowQuickInfo (); } @@ -268,37 +268,37 @@ namespace MonoDevelop.Ide.Editor public static void JoinLines (TextEditor textEditor) { - textEditor.EditorActionHost.JoinLines (); + ((IMonoDevelopEditorOperations)textEditor.EditorOperations).JoinLines (); } public static void RecenterEditor (TextEditor textEditor) { - textEditor.EditorActionHost.RecenterEditor (); + textEditor.EditorOperations.ScrollLineCenter (); } public static void StartCaretPulseAnimation (TextEditor textEditor) { - textEditor.EditorActionHost.StartCaretPulseAnimation (); + ((IMonoDevelopEditorOperations)textEditor.EditorOperations).StartCaretPulseAnimation (); } public static void DeleteNextSubword (TextEditor textEditor) { - textEditor.EditorActionHost.DeleteNextSubword (); + ((IMonoDevelopEditorOperations)textEditor.EditorOperations).DeleteNextSubword (); } public static void DeletePreviousSubword (TextEditor textEditor) { - textEditor.EditorActionHost.DeletePreviousSubword (); + ((IMonoDevelopEditorOperations)textEditor.EditorOperations).DeletePreviousSubword (); } public static void DeleteNextWord (TextEditor textEditor) { - textEditor.EditorActionHost.DeleteNextWord (); + textEditor.EditorOperations.DeleteWordToRight (); } public static void DeletePreviousWord (TextEditor textEditor) { - textEditor.EditorActionHost.DeletePreviousWord (); + textEditor.EditorOperations.DeleteWordToLeft (); } public static void InsertNewLinePreserveCaretPosition (TextEditor textEditor) @@ -324,47 +324,47 @@ namespace MonoDevelop.Ide.Editor public static void InsertNewLine (TextEditor textEditor) { - textEditor.EditorActionHost.InsertNewLine (); + textEditor.EditorOperations.InsertNewLine (); } public static void RemoveTab (TextEditor textEditor) { - textEditor.EditorActionHost.RemoveTab (); + textEditor.EditorOperations.Untabify (); } public static void InsertTab (TextEditor textEditor) { - textEditor.EditorActionHost.InsertTab (); + textEditor.EditorOperations.Tabify (); } public static void SwitchCaretMode (TextEditor textEditor) { - textEditor.EditorActionHost.SwitchCaretMode (); + ((IMonoDevelopEditorOperations)textEditor.EditorOperations).SwitchCaretMode (); } public static void MoveBlockUp (TextEditor textEditor) { - textEditor.EditorActionHost.MoveBlockUp (); + ((IMonoDevelopEditorOperations)textEditor.EditorOperations).MoveBlockUp (); } public static void MoveBlockDown (TextEditor textEditor) { - textEditor.EditorActionHost.MoveBlockDown (); + ((IMonoDevelopEditorOperations)textEditor.EditorOperations).MoveBlockDown (); } public static void ToggleBlockSelectionMode (TextEditor textEditor) { - textEditor.EditorActionHost.ToggleBlockSelectionMode (); + ((IMonoDevelopEditorOperations)textEditor.EditorOperations).ToggleBlockSelectionMode (); } public static void IndentSelection (TextEditor editor) { - editor.EditorActionHost.IndentSelection (); + editor.EditorOperations.Indent (); } public static void UnIndentSelection (TextEditor editor) { - editor.EditorActionHost.UnIndentSelection (); + editor.EditorOperations.Unindent (); } #region SelectionActions diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditorConfigService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditorConfigService.cs new file mode 100644 index 0000000000..1f4ee97422 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/EditorConfigService.cs @@ -0,0 +1,134 @@ +ο»Ώ// +// EditorConfigService.cs +// +// Author: +// Mike KrΓΌger <mikkrg@microsoft.com> +// +// Copyright (c) 2017 Microsoft +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Immutable; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.CodingConventions; +using System.Collections.Generic; +using MonoDevelop.Core; + +namespace MonoDevelop.Ide.Editor +{ + static class EditorConfigService + { + public readonly static string MaxLineLengthConvention = "max_line_length"; + readonly static object contextCacheLock = new object (); + readonly static ICodingConventionsManager codingConventionsManager = CodingConventionsManagerFactory.CreateCodingConventionsManager (new ConventionsFileManager()); + static ImmutableDictionary<string, ICodingConventionContext> contextCache = ImmutableDictionary<string, ICodingConventionContext>.Empty; + + public async static Task<ICodingConventionContext> GetEditorConfigContext (string fileName, CancellationToken token = default (CancellationToken)) + { + if (!File.Exists (fileName)) + return null; + if (contextCache.TryGetValue (fileName, out ICodingConventionContext result)) + return result; + try { + result = await codingConventionsManager.GetConventionContextAsync (fileName, token); + } catch (OperationCanceledException) { + } catch (Exception e) { + LoggingService.LogError ("Error while getting coding conventions,", e); + } + if (result == null) + return null; + lock (contextCacheLock) { + contextCache = contextCache.SetItem (fileName, result); + return result; + } + } + + public static void RemoveEditConfigContext (string fileName) + { + lock (contextCacheLock) { + contextCache = contextCache.Remove (fileName); + } + } + + class ConventionsFileManager : IFileWatcher + { + Dictionary<string, FileSystemWatcher> watchers = new Dictionary<string, FileSystemWatcher> (); + + public event ConventionsFileChangedAsyncEventHandler ConventionFileChanged; + public event ContextFileMovedAsyncEventHandler ContextFileMoved; + + public void Dispose () + { + lock (watchers) { + foreach (var kv in watchers) + kv.Value.Dispose (); + watchers = null; + } + } + + void OnChanged (object source, FileSystemEventArgs e) + { + var watcher = (FileSystemWatcher)source; + ConventionFileChanged?.Invoke (this, new ConventionsFileChangeEventArgs (watcher.Filter, watcher.Path, GetChangeType(e.ChangeType))); + } + + static ChangeType GetChangeType(WatcherChangeTypes type) + { + switch (type) { + case WatcherChangeTypes.Changed: + return ChangeType.FileModified; + case WatcherChangeTypes.Deleted: + return ChangeType.FileDeleted; + } + return ChangeType.FileModified; + } + + public void StartWatching (string fileName, string directoryPath) + { + lock (watchers) { + var key = directoryPath + Path.DirectorySeparatorChar.ToString () + fileName; + + if (watchers.ContainsKey (key)) + return; + + var watcher = new FileSystemWatcher (); + watcher.Path = directoryPath; + watcher.Filter = fileName; + watcher.Changed += OnChanged; + watcher.Deleted += OnChanged; + watcher.EnableRaisingEvents = true; + watchers.Add (key, watcher); + } + } + + public void StopWatching (string fileName, string directoryPath) + { + lock (watchers) { + var key = directoryPath + Path.DirectorySeparatorChar.ToString () + fileName; + if (watchers.TryGetValue (key, out FileSystemWatcher watcher)) { + watcher.Dispose (); + watchers.Remove (key); + } + } + } + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IEditorActionHost.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IMonoDevelopEditorOperations.cs index 81fccd8303..3819dc85ab 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IEditorActionHost.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/IMonoDevelopEditorOperations.cs @@ -28,98 +28,32 @@ using MonoDevelop.Core.Text; namespace MonoDevelop.Ide.Editor { - interface IEditorActionHost + interface IMonoDevelopEditorOperations : Microsoft.VisualStudio.Text.Operations.IEditorOperations { void SwitchCaretMode (); - void InsertTab (); - - void RemoveTab (); - - void InsertNewLine (); - - void DeletePreviousWord (); - - void DeleteNextWord (); - void DeletePreviousSubword (); void DeleteNextSubword (); void StartCaretPulseAnimation (); - void RecenterEditor (); - void JoinLines (); - void MoveNextSubWord (); - - void MovePrevSubWord (); - - void MoveNextWord (); - - void MovePrevWord (); - - void PageUp (); - - void PageDown (); - - void MoveCaretDown (); - - void MoveCaretUp (); - - void MoveCaretRight (); - - void MoveCaretLeft (); - - void MoveCaretToLineEnd (); - - void MoveCaretToLineStart (); + void MoveToNextSubWord (); - void MoveCaretToDocumentStart (); - - void MoveCaretToDocumentEnd (); - - void Backspace (); - - void Delete (); - - void ClipboardCopy (); - - void ClipboardCut (); - - void ClipboardPaste (); - - void SelectAll (); - - void NewLine (); + void MoveToPrevSubWord (); void Undo (); void Redo (); - void DeleteCurrentLine (); - - void DeleteCurrentLineToEnd (); - - void ScrollLineUp (); - - void ScrollLineDown (); - - void ScrollPageUp (); - - void ScrollPageDown (); - void MoveBlockUp (); void MoveBlockDown (); void ToggleBlockSelectionMode (); - void IndentSelection (); - - void UnIndentSelection (); - void ShowQuickInfo (); } }
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs index e0d1a9d7b3..36eae1181f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/InternalExtensionAPI/ITextEditorImpl.cs @@ -46,6 +46,8 @@ namespace MonoDevelop.Ide.Editor interface ITextEditorImpl : IDisposable { + Microsoft.VisualStudio.Text.Editor.ITextView TextView { get; set; } + ViewContent ViewContent { get; } string ContentName { get; set; } @@ -98,7 +100,7 @@ namespace MonoDevelop.Ide.Editor void FixVirtualIndentation (); - IEditorActionHost Actions { get; } + IMonoDevelopEditorOperations Actions { get; } ITextMarkerFactory TextMarkerFactory { get; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedDocumentContext.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedDocumentContext.cs index b2fa634440..441f46cedc 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedDocumentContext.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/Projection/ProjectedDocumentContext.cs @@ -30,6 +30,7 @@ using MonoDevelop.Core.Text; using MonoDevelop.Ide.Gui; using MonoDevelop.Ide.TypeSystem; using System.Threading.Tasks; +using MonoDevelop.Core; namespace MonoDevelop.Ide.Editor.Projection { @@ -101,7 +102,7 @@ namespace MonoDevelop.Ide.Editor.Projection ReparseDocumentInternal (); } - Task ReparseDocumentInternal () + async Task ReparseDocumentInternal () { var options = new ParseOptions { FileName = projectedEditor.FileName, @@ -110,8 +111,9 @@ namespace MonoDevelop.Ide.Editor.Projection RoslynDocument = projectedDocument, OldParsedDocument = parsedDocument }; - return TypeSystemService.ParseFile (options, projectedEditor.MimeType).ContinueWith (t => { - parsedDocument = t.Result; + var result = await TypeSystemService.ParseFile (options, projectedEditor.MimeType).ConfigureAwait (false); + await Runtime.RunInMainThread (delegate { + parsedDocument = result; base.OnDocumentParsed (EventArgs.Empty); }); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs index 3e67d61419..01df4804d9 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditor.cs @@ -50,7 +50,7 @@ namespace MonoDevelop.Ide.Editor public sealed class TextEditor : Control, ITextDocument, IDisposable { readonly ITextEditorImpl textEditorImpl; - public Microsoft.VisualStudio.Text.Editor.ITextView TextView { get; } + public Microsoft.VisualStudio.Text.Editor.ITextView TextView { get => textEditorImpl.TextView; set => textEditorImpl.TextView = value; } IReadonlyTextDocument ReadOnlyTextDocument { get { return textEditorImpl.Document; } } @@ -970,6 +970,8 @@ namespace MonoDevelop.Ide.Editor if (isDisposed) return; Runtime.AssertMainThread (); + this.TextView.Close (); + // Break fileTypeCondition circular event handling reference. fileTypeCondition = null; isDisposed = true; @@ -980,8 +982,6 @@ namespace MonoDevelop.Ide.Editor provider.Dispose (); textEditorImpl.Dispose (); - this.TextView.Close();
- base.Dispose (disposing); } @@ -1002,7 +1002,7 @@ namespace MonoDevelop.Ide.Editor } } - internal IEditorActionHost EditorActionHost { + internal Microsoft.VisualStudio.Text.Operations.IEditorOperations EditorOperations { get { return textEditorImpl.Actions; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs index ba49053020..f3f90d19b6 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorDisplayBinding.cs @@ -97,18 +97,12 @@ namespace MonoDevelop.Ide.Editor {
TextEditor editor;
- // HACK: this is a very poor test for whether to load the document. Maybe add an IsPlaceholder property to FilePath.
- // Another alternative would be to add a parameter to CreateContent.
- if (File.Exists(fileName))
- {
- editor = TextEditorFactory.CreateNewEditor(fileName, mimeType);
- }
- else
- {
- editor = TextEditorFactory.CreateNewEditor();
- editor.FileName = fileName; - editor.MimeType = mimeType;
- }
+ // HACK: CreateNewEditor really needs to know whether the document exists (& should be loaded)
+ // or we're creating an empty document with the given file name & mime type.
+ //
+ // That information could be added to FilePath but fileName is converted to a string below
+ // which means the information is lost.
+ editor = TextEditorFactory.CreateNewEditor(fileName, mimeType);
editor.GetViewContent ().Project = ownerProject; return editor.GetViewContent (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs index dab769f786..c720e8daf2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs @@ -48,6 +48,7 @@ using Microsoft.CodeAnalysis; using Gdk; using MonoDevelop.Ide.CodeFormatting; using System.Collections.Immutable; +using Microsoft.VisualStudio.CodingConventions; namespace MonoDevelop.Ide.Editor { @@ -73,7 +74,7 @@ namespace MonoDevelop.Ide.Editor this.textEditor.MimeTypeChanged += UpdateTextEditorOptions; DefaultSourceEditorOptions.Instance.Changed += UpdateTextEditorOptions; textEditorImpl.ViewContent.ContentNameChanged += ViewContent_ContentNameChanged; - textEditorImpl.ViewContent.DirtyChanged += ViewContent_DirtyChanged; ; + textEditorImpl.ViewContent.DirtyChanged += ViewContent_DirtyChanged; } @@ -82,9 +83,12 @@ namespace MonoDevelop.Ide.Editor base.OnContentNameChanged (); if (ContentName != textEditorImpl.ContentName && !string.IsNullOrEmpty (textEditorImpl.ContentName)) AutoSave.RemoveAutoSaveFile (textEditorImpl.ContentName); + if (textEditorImpl.ContentName != null) + EditorConfigService.RemoveEditConfigContext (textEditorImpl.ContentName); textEditorImpl.ContentName = this.ContentName; if (this.WorkbenchWindow?.Document != null) textEditor.InitializeExtensionChain (this.WorkbenchWindow.Document); + UpdateTextEditorOptions (null, null); } void ViewContent_ContentNameChanged (object sender, EventArgs e) @@ -145,7 +149,7 @@ namespace MonoDevelop.Ide.Editor policyContainer.PolicyChanged -= HandlePolicyChanged; } - void UpdateStyleParent (MonoDevelop.Projects.Project styleParent, string mimeType) + async Task UpdateStyleParent (MonoDevelop.Projects.Project styleParent, string mimeType) { RemovePolicyChangeHandler (); @@ -161,7 +165,13 @@ namespace MonoDevelop.Ide.Editor var currentPolicy = policyContainer.Get<TextStylePolicy> (mimeTypes); policyContainer.PolicyChanged += HandlePolicyChanged; - textEditor.Options = DefaultSourceEditorOptions.Instance.WithTextStyle (currentPolicy); + + var context = await EditorConfigService.GetEditorConfigContext (textEditor.FileName, default (CancellationToken)); + if (context == null) + return; + var options = DefaultSourceEditorOptions.Instance.WithTextStyle (currentPolicy); + options.SetContext (context); + textEditor.Options = options; } void HandlePolicyChanged (object sender, MonoDevelop.Projects.Policies.PolicyChangedEventArgs args) @@ -336,6 +346,7 @@ namespace MonoDevelop.Ide.Editor base.Dispose (); isDisposed = true; + EditorConfigService.RemoveEditConfigContext (textEditor.FileName); CancelDocumentParsedUpdate (); textEditorImpl.ViewContent.DirtyChanged -= HandleDirtyChanged; textEditor.MimeTypeChanged -= UpdateTextEditorOptions; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Execution/MonoExecutionParametersPreview.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Execution/MonoExecutionParametersPreview.cs index ba33b9cea5..cc7ef21975 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Execution/MonoExecutionParametersPreview.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Execution/MonoExecutionParametersPreview.cs @@ -44,7 +44,7 @@ namespace MonoDevelop.Ide.Execution Dictionary<string,string> vars = new Dictionary<string, string> (); options.GenerateOptions (vars, out cmd); - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); if (cmd.Length == 0 && vars.Count == 0) { sb.AppendLine (GLib.Markup.EscapeText (GettextCatalog.GetString ("No options have been specified."))); @@ -68,7 +68,7 @@ namespace MonoDevelop.Ide.Execution sb.AppendLine (svar); } - labelOps.Markup = sb.ToString (); + labelOps.Markup = StringBuilderCache.ReturnAndFree (sb); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/MimeTypeNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/MimeTypeNode.cs index 441ddf81d6..71b486b1eb 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/MimeTypeNode.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/MimeTypeNode.cs @@ -103,7 +103,7 @@ namespace MonoDevelop.Ide.Extensions Regex CreateRegex (MimeTypeNode node) { - var globalPattern = new StringBuilder (); + var globalPattern = StringBuilderCache.Allocate (); foreach (MimeTypeFileNode file in node.ChildNodes) { string pattern = Regex.Escape (file.Pattern); @@ -115,7 +115,7 @@ namespace MonoDevelop.Ide.Extensions globalPattern.Append ('|'); globalPattern.Append (pattern); } - return new Regex (globalPattern.ToString (), RegexOptions.IgnoreCase); + return new Regex (StringBuilderCache.ReturnAndFree (globalPattern), RegexOptions.IgnoreCase); } public bool SupportsFile (string fileName) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs index 3b2d881171..58f710ad14 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs @@ -500,20 +500,7 @@ namespace MonoDevelop.Ide.FindInFiles Visible = true, Ready = true, }; - - var checkMenuItem = searchentryFileMask.AddFilterOption (0, GettextCatalog.GetString ("Include binary files")); - checkMenuItem.DrawAsRadio = false; - checkMenuItem.Active = properties.Get ("IncludeBinaryFiles", false); - checkMenuItem.Toggled += delegate { - properties.Set ("IncludeBinaryFiles", checkMenuItem.Active); - }; - - var checkMenuItem1 = searchentryFileMask.AddFilterOption (1, GettextCatalog.GetString ("Include hidden files and directories")); - checkMenuItem1.DrawAsRadio = false; - checkMenuItem1.Active = properties.Get ("IncludeHiddenFiles", false); - checkMenuItem1.Toggled += delegate { - properties.Set ("IncludeHiddenFiles", checkMenuItem1.Active); - }; + searchentryFileMask.Query = properties.Get ("MonoDevelop.FindReplaceDialogs.FileMask", ""); @@ -793,15 +780,12 @@ namespace MonoDevelop.Ide.FindInFiles return null; } - scope = new DirectoryScope (comboboxentryPath.Entry.Text, checkbuttonRecursively.Active) { - IncludeHiddenFiles = properties.Get ("IncludeHiddenFiles", false) - }; + scope = new DirectoryScope (comboboxentryPath.Entry.Text, checkbuttonRecursively.Active); break; default: throw new ApplicationException ("Unknown scope:" + comboboxScope.Active); } - scope.IncludeBinaryFiles = properties.Get ("IncludeBinaryFiles", false); return scope; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs index 9da97c4c0b..19a08d442f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs @@ -39,6 +39,7 @@ namespace MonoDevelop.Ide.FindInFiles { public abstract class Scope { + [Obsolete ("Unused - will be removed")] public bool IncludeBinaryFiles { get; set; @@ -145,7 +146,7 @@ namespace MonoDevelop.Ide.FindInFiles () => new List<FileProvider> (), (folder, loop, providers) => { foreach (var file in folder.Files.Where (f => filterOptions.NameMatches (f.FileName) && File.Exists (f.FullPath))) { - if (!IncludeBinaryFiles && !DesktopService.GetFileIsText (file.FullPath)) + if (!DesktopService.GetFileIsText (file.FullPath)) continue; lock (alreadyVisited) { if (alreadyVisited.Contains (file.FullPath)) @@ -171,7 +172,7 @@ namespace MonoDevelop.Ide.FindInFiles foreach (ProjectFile file in project.GetSourceFilesAsync (conf).Result.Where (f => filterOptions.NameMatches (f.Name) && File.Exists (f.Name))) { if ((file.Flags & ProjectItemFlags.Hidden) == ProjectItemFlags.Hidden) continue; - if (!IncludeBinaryFiles && !DesktopService.GetFileIsText (file.FilePath)) + if (!DesktopService.GetFileIsText (file.FilePath)) continue; lock (alreadyVisited) { @@ -227,7 +228,7 @@ namespace MonoDevelop.Ide.FindInFiles foreach (ProjectFile file in project.GetSourceFilesAsync (conf).Result.Where (f => filterOptions.NameMatches (f.Name) && File.Exists (f.Name))) { if ((file.Flags & ProjectItemFlags.Hidden) == ProjectItemFlags.Hidden) continue; - if (!IncludeBinaryFiles && !DesktopService.GetFileIsText (file.Name)) + if (!DesktopService.GetFileIsText (file.Name)) continue; if (alreadyVisited.Contains (file.FilePath.FullPath)) continue; @@ -280,6 +281,7 @@ namespace MonoDevelop.Ide.FindInFiles get { return PathMode.Absolute; } } + [Obsolete ("Unused - will be removed")] public bool IncludeHiddenFiles { get; set; @@ -315,33 +317,29 @@ namespace MonoDevelop.Ide.FindInFiles } foreach (string fileName in Directory.EnumerateFiles (curPath, "*")) { - if (!IncludeHiddenFiles) { - if (Platform.IsWindows) { - var attr = File.GetAttributes (fileName); - if (attr.HasFlag (FileAttributes.Hidden)) - continue; - } - if (Path.GetFileName (fileName).StartsWith (".", StringComparison.Ordinal)) + if (Platform.IsWindows) { + var attr = File.GetAttributes (fileName); + if (attr.HasFlag (FileAttributes.Hidden)) continue; } + if (Path.GetFileName (fileName).StartsWith (".", StringComparison.Ordinal)) + continue; if (!filterOptions.NameMatches (fileName)) continue; - if (!IncludeBinaryFiles && !DesktopService.GetFileIsText (fileName)) + if (!DesktopService.GetFileIsText (fileName)) continue; yield return fileName; } if (recurse) { foreach (string directoryName in Directory.EnumerateDirectories (curPath)) { - if (!IncludeHiddenFiles) { - if (Platform.IsWindows) { - var attr = File.GetAttributes (directoryName); - if (attr.HasFlag (FileAttributes.Hidden)) - continue; - } - if (Path.GetFileName (directoryName).StartsWith (".", StringComparison.Ordinal)) + if (Platform.IsWindows) { + var attr = File.GetAttributes (directoryName); + if (attr.HasFlag (FileAttributes.Hidden)) continue; } + if (Path.GetFileName (directoryName).StartsWith (".", StringComparison.Ordinal)) + continue; directoryStack.Push (directoryName); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/LogView.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/LogView.cs index f3c7aefd5a..4ec206f18c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/LogView.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Components/LogView.cs @@ -804,6 +804,8 @@ namespace MonoDevelop.Ide.Gui.Components } internal class IndentTracker { + static int trackerID = 0; + List<TextTag> tags = new List<TextTag> (); public TextTag IndentTag; @@ -814,7 +816,12 @@ namespace MonoDevelop.Ide.Gui.Components indent++; if (indent >= tags.Count) { - tag = new TextTag (indent.ToString ()); + // create a unique tagname + // Fixes VSTS 584931 + tag = new TextTag ($"{trackerID}-{indent}"); + System.Console.WriteLine ($"{tag.Name}"); + trackerID++; + tag.LeftMargin = 10 + 15 * (indent - 1); tags.Add (tag); } else { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/TextStylePolicy.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/TextStylePolicy.cs index 71b51040d2..e94cb3b05f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/TextStylePolicy.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Content/TextStylePolicy.cs @@ -100,15 +100,24 @@ namespace MonoDevelop.Ide.Gui.Content }; } - public TextStylePolicy WithTabWidth(int tabWidth) + public TextStylePolicy WithTabWidth (int tabWidth) { if (tabWidth == TabWidth) return this; - return new TextStylePolicy(this) { + return new TextStylePolicy (this) { TabWidth = tabWidth }; } + public TextStylePolicy WithEolMarker (EolMarker eolMarker) + { + if (eolMarker == EolMarker) + return this; + return new TextStylePolicy (this) { + EolMarker = eolMarker + }; + } + public static string GetEolMarker (EolMarker eolMarker) { switch (eolMarker) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectFileNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectFileNodeBuilder.cs index d8f55c4c31..6e7c406db5 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectFileNodeBuilder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/ProjectFileNodeBuilder.cs @@ -155,7 +155,6 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad public async override void RenameItem (string newName) { - ProjectFile newProjectFile = null; var file = (ProjectFile) CurrentNode.DataItem; string oldFileName = file.FilePath; @@ -163,26 +162,24 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad if (oldFileName == newFileName) return; - FilePath newPath, newLink = FilePath.Null; - if (file.IsLink) { - var oldLink = file.ProjectVirtualPath; - newLink = oldLink.ParentDirectory.Combine (newName); - newPath = file.Project.BaseDirectory.Combine (newLink); - } else { - newPath = file.FilePath.ParentDirectory.Combine (newName); - } - + var dependentFilesToRename = GetDependentFilesToRename (file, newName); + try { - if (file.Project != null) - newProjectFile = file.Project.Files.GetFileWithVirtualPath (newPath.ToRelative (file.Project.BaseDirectory)); - - if (!FileService.IsValidPath (newPath) || ProjectFolderCommandHandler.ContainsDirectorySeparator (newName)) { - MessageService.ShowWarning (GettextCatalog.GetString ("The name you have chosen contains illegal characters. Please choose a different name.")); - } else if ((newProjectFile != null && newProjectFile != file) || FileExistsCaseSensitive (file.FilePath.ParentDirectory, newName)) { - // If there is already a file under the newPath which is *different*, then throw an exception - MessageService.ShowWarning (GettextCatalog.GetString ("File or directory name is already in use. Please choose a different one.")); - } else { + if (CanRenameFile (file, newName)) { + if (dependentFilesToRename != null) { + if (dependentFilesToRename.Any (f => !CanRenameFile (f.File, f.NewName))) { + return; + } + } + FileService.RenameFile (file.FilePath, newName); + + if (dependentFilesToRename != null) { + foreach (var dependentFile in dependentFilesToRename) { + FileService.RenameFile (dependentFile.File.FilePath, dependentFile.NewName); + } + } + if (file.Project != null) await IdeApp.ProjectOperations.SaveAsync (file.Project); } @@ -193,6 +190,60 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad } } + static FilePath GetRenamedFilePath (ProjectFile file, string newName) + { + if (file.IsLink) { + var oldLink = file.ProjectVirtualPath; + var newLink = oldLink.ParentDirectory.Combine (newName); + return file.Project.BaseDirectory.Combine (newLink); + } + return file.FilePath.ParentDirectory.Combine (newName); + } + + /// <summary> + /// Returns all dependent files that have names that start with the old name of the file. + /// </summary> + static List<(ProjectFile File, string NewName)> GetDependentFilesToRename (ProjectFile file, string newName) + { + if (!file.HasChildren) + return null; + + List<(ProjectFile File, string NewName)> files = null; + + string oldName = file.FilePath.FileName; + foreach (ProjectFile child in file.DependentChildren) { + string oldChildName = child.FilePath.FileName; + if (oldChildName.StartsWith (oldName, StringComparison.CurrentCultureIgnoreCase)) { + string childNewName = newName + oldChildName.Substring (oldName.Length); + + if (files == null) + files = new List<(ProjectFile projectFile, string name)> (); + files.Add ((child, childNewName)); + } + } + return files; + } + + static bool CanRenameFile (ProjectFile file, string newName) + { + ProjectFile newProjectFile = null; + FilePath newPath = GetRenamedFilePath (file, newName); + + if (file.Project != null) + newProjectFile = file.Project.Files.GetFileWithVirtualPath (newPath.ToRelative (file.Project.BaseDirectory)); + + if (!FileService.IsValidPath (newPath) || ProjectFolderCommandHandler.ContainsDirectorySeparator (newName)) { + MessageService.ShowWarning (GettextCatalog.GetString ("The name you have chosen contains illegal characters. Please choose a different name.")); + return false; + } else if ((newProjectFile != null && newProjectFile != file) || FileExistsCaseSensitive (file.FilePath.ParentDirectory, newName)) { + // If there is already a file under the newPath which is *different*, then throw an exception + MessageService.ShowWarning (GettextCatalog.GetString ("File or directory name is already in use. Please choose a different one.")); + return false; + } + + return true; + } + static bool FileExistsCaseSensitive (FilePath parentDirectory, string fileName) { if (!Directory.Exists (parentDirectory)) @@ -393,9 +444,10 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad { var toggledActions = new Set<string> (); Project proj = null; + ProjectFile finfo = null; foreach (var node in CurrentNodes) { - var finfo = (ProjectFile) node.DataItem; + finfo = (ProjectFile) node.DataItem; //disallow multi-slect on more than one project, since available build actions may differ if (proj == null && finfo.Project != null) { @@ -410,7 +462,7 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad if (proj == null) return; - foreach (string action in proj.GetBuildActions ()) { + foreach (string action in proj.GetBuildActions (finfo.FilePath)) { if (action == "--") { info.AddSeparator (); } else { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Document.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Document.cs index 6abe1511a5..7d1f8c923a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Document.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Document.cs @@ -235,7 +235,6 @@ namespace MonoDevelop.Ide.Gui get { return adhocProject != null; } } - public override bool IsCompileableInProject { get { var project = Project; @@ -431,6 +430,7 @@ namespace MonoDevelop.Ide.Gui await Window.ViewContent.Save (fileName + "~");
FileService.NotifyFileChanged (fileName + "~"); }
+ DocumentRegistry.SkipNextChange (fileName); await Window.ViewContent.Save (fileName);
FileService.NotifyFileChanged (fileName); OnSaved(EventArgs.Empty); @@ -838,6 +838,11 @@ namespace MonoDevelop.Ide.Gui analysisDocumentSrc = new CancellationTokenSource (); } + /// <summary> + /// During that process ad hoc projects shouldn't be created. + /// </summary> + internal static bool IsInProjectSettingLoadingProcess { get; set; } + Task EnsureAnalysisDocumentIsOpen () { if (analysisDocument != null) { @@ -862,16 +867,16 @@ namespace MonoDevelop.Ide.Gui return Task.CompletedTask; SubscribeRoslynWorkspace (); analysisDocument = FileName != null ? TypeSystemService.GetDocumentId (this.Project, this.FileName) : null; - if (analysisDocument != null) { + if (analysisDocument != null && !RoslynWorkspace.IsDocumentOpen(analysisDocument)) { TypeSystemService.InformDocumentOpen (analysisDocument, Editor); OnAnalysisDocumentChanged (EventArgs.Empty); - return Task.CompletedTask; } + return Task.CompletedTask; } } lock (adhocProjectLock) { var token = analysisDocumentSrc.Token; - if (adhocProject != null) { + if (adhocProject != null || IsInProjectSettingLoadingProcess) { return Task.CompletedTask; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ViewCommandHandlers.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ViewCommandHandlers.cs index fd2177cd6f..864ca29f0e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ViewCommandHandlers.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/ViewCommandHandlers.cs @@ -359,61 +359,61 @@ namespace MonoDevelop.Ide.Gui [CommandHandler (TextEditorCommands.LineEnd)] protected void OnLineEnd () { - doc.Editor.EditorActionHost.MoveCaretToLineEnd (); + doc.Editor.EditorOperations.MoveToEndOfLine (false); } [CommandHandler (TextEditorCommands.LineStart)] protected void OnLineStart () { - doc.Editor.EditorActionHost.MoveCaretToLineStart (); + doc.Editor.EditorOperations.MoveToStartOfLine (false); } [CommandHandler (TextEditorCommands.DeleteLeftChar)] protected void OnDeleteLeftChar () { - doc.Editor.EditorActionHost.Backspace (); + doc.Editor.EditorOperations.Backspace (); } [CommandHandler (TextEditorCommands.DeleteRightChar)] protected void OnDeleteRightChar () { - doc.Editor.EditorActionHost.Delete (); + doc.Editor.EditorOperations.Delete (); } [CommandHandler (TextEditorCommands.CharLeft)] protected void OnCharLeft () { - doc.Editor.EditorActionHost.MoveCaretLeft (); + doc.Editor.EditorOperations.MoveToPreviousCharacter (false); } [CommandHandler (TextEditorCommands.CharRight)] protected void OnCharRight () { - doc.Editor.EditorActionHost.MoveCaretRight (); + doc.Editor.EditorOperations.MoveToNextCharacter (false); } [CommandHandler (TextEditorCommands.LineUp)] protected void OnLineUp () { - doc.Editor.EditorActionHost.MoveCaretUp (); + doc.Editor.EditorOperations.MoveLineUp (false); } [CommandHandler (TextEditorCommands.LineDown)] protected void OnLineDown () { - doc.Editor.EditorActionHost.MoveCaretDown (); + doc.Editor.EditorOperations.MoveLineDown (false); } [CommandHandler (TextEditorCommands.DocumentStart)] protected void OnDocumentStart () { - doc.Editor.EditorActionHost.MoveCaretToDocumentStart (); + doc.Editor.EditorOperations.MoveToStartOfDocument (false); } [CommandHandler (TextEditorCommands.DocumentEnd)] protected void OnDocumentEnd () { - doc.Editor.EditorActionHost.MoveCaretToDocumentEnd (); + doc.Editor.EditorOperations.MoveToEndOfDocument (false); } [CommandHandler (TextEditorCommands.DeleteLine)] diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/BaseDirectoryPanelWidget.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/BaseDirectoryPanelWidget.cs index 7f0ed03eca..87763fbec2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/BaseDirectoryPanelWidget.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects.OptionPanels/BaseDirectoryPanelWidget.cs @@ -41,6 +41,8 @@ namespace MonoDevelop.Ide.Projects.OptionPanels public BaseDirectoryPanelWidget() { this.Build(); + + label2.Accessible.SetShouldIgnore (true); var a = folderentry.EntryAccessible; a.SetTitleUIElement (label3.Accessible); label3.Accessible.SetTitleFor (a); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs index 876fd4dc31..c301f2cbbf 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs @@ -146,7 +146,7 @@ namespace MonoDevelop.Ide.Projects var sorted = content.ToList (); sorted.Sort ((x, y) => x.Key.CompareTo(y.Key)); - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); foreach (var pol in sorted) { if (sb.Length > 0) @@ -168,7 +168,7 @@ namespace MonoDevelop.Ide.Projects sb.Append (")"); } } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } protected void OnCombPoliciesChanged (object sender, System.EventArgs e) @@ -256,7 +256,7 @@ namespace MonoDevelop.Ide.Projects store.Clear (); - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); foreach (var pol in sorted) { sb.Append (pol.Key); if (pol.Value.Count != 1 || pol.Value[0].Length != 0) { @@ -277,7 +277,7 @@ namespace MonoDevelop.Ide.Projects store.AppendValues (sb.ToString ()); sb.Length = 0; } - + StringBuilderCache.Free (sb); HasPolicies = sorted.Count > 0; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/AssemblyReferencePanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/AssemblyReferencePanel.cs index a87031db12..a1b91bf4ca 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/AssemblyReferencePanel.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/AssemblyReferencePanel.cs @@ -285,7 +285,7 @@ namespace MonoDevelop.Ide.Projects internal static string GetMatchMarkup (Gtk.Widget widget, string text, int[] matches, int startIndex) { - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); int lastPos = 0; var color = HslColor.GenerateHighlightColors (widget.Style.Base (StateType.Normal), widget.Style.Text (StateType.Normal), 3)[2]; @@ -304,7 +304,7 @@ namespace MonoDevelop.Ide.Projects } if (lastPos < text.Length) result.Append (GLib.Markup.EscapeText (text.Substring (lastPos, text.Length - lastPos))); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public void AddReference (object sender, Gtk.ToggledArgs e) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/IdeFileSystemExtensionExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/IdeFileSystemExtensionExtension.cs index 1472f82945..52ef22a80f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/IdeFileSystemExtensionExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/IdeFileSystemExtensionExtension.cs @@ -63,8 +63,7 @@ namespace MonoDevelop.Ide.Projects string error = GettextCatalog.GetString ("File {0} is read-only", file.FileName); - var btn = new AlertButton (GettextCatalog.GetString ("Make Writable")); - var res = MessageService.AskQuestion (error, GettextCatalog.GetString ("Would you like {0} to attempt to make the file writable and try again?", BrandingService.ApplicationName), btn, AlertButton.Cancel); + var res = MessageService.AskQuestion (error, GettextCatalog.GetString ("Would you like {0} to attempt to make the file writable and try again?", BrandingService.ApplicationName), AlertButton.MakeWriteable, AlertButton.Cancel); if (res == AlertButton.Cancel) throw new UserException (error) { AlreadyReportedToUser = true }; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/NewFileDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/NewFileDialog.cs index 4165abbf21..6f46230c8b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/NewFileDialog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/NewFileDialog.cs @@ -507,7 +507,7 @@ namespace MonoDevelop.Ide.Projects public event EventHandler OnOked; - void OpenEvent (object sender, EventArgs e) + async void OpenEvent (object sender, EventArgs e) { if (!okButton.Sensitive) return; @@ -531,7 +531,7 @@ namespace MonoDevelop.Ide.Projects } try { - if (!item.Create (project, project, path, titem.Language, filename)) + if (!await item.Create (project, project, path, titem.Language, filename)) return; } catch (Exception ex) { LoggingService.LogError ("Error creating file", ex); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.StandardHeader/StandardHeaderService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.StandardHeader/StandardHeaderService.cs index f3692a002d..4f78ec6d34 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.StandardHeader/StandardHeaderService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.StandardHeader/StandardHeaderService.cs @@ -70,7 +70,7 @@ namespace MonoDevelop.Ide.StandardHeader if (!char.IsWhiteSpace (cmt[cmt.Length - 1])) cmt = cmt + " "; - StringBuilder sb = new StringBuilder (policy.Text.Length); + StringBuilder sb = StringBuilderCache.Allocate (); string[] lines = policy.Text.Split ('\n'); foreach (string line in lines) { if (string.IsNullOrWhiteSpace (line)) { @@ -82,7 +82,7 @@ namespace MonoDevelop.Ide.StandardHeader sb.Append (line); sb.Append (eolMarker); } - result = sb.ToString (); + result = StringBuilderCache.ReturnAndFree (sb); } else { //multiline comment result = String.Concat (comment[0], "\n", policy.Text, "\n", comment[1], "\n"); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/DirectoryTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/DirectoryTemplate.cs index be481a9ca5..7e6aa7d606 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/DirectoryTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/DirectoryTemplate.cs @@ -30,6 +30,7 @@ using System.Xml; using System.IO; using MonoDevelop.Core; using MonoDevelop.Projects; +using System.Threading.Tasks; namespace MonoDevelop.Ide.Templates { @@ -85,7 +86,7 @@ namespace MonoDevelop.Ide.Templates t.Show (); } - public override bool AddToProject (SolutionFolderItem policyParent, Project project, + public override async Task<bool> AddToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string name) { bool addedSomething = false; @@ -97,6 +98,26 @@ namespace MonoDevelop.Ide.Templates } foreach (FileDescriptionTemplate t in templates) { + if (t.EvaluateCreateCondition ()) { + addedSomething |= await t.AddToProjectAsync (policyParent, project, language, directory, name); + } + } + return addedSomething; + } + + [Obsolete ("Use public Task<bool> AddToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string entryName).")] + public override bool AddToProject (SolutionFolderItem policyParent, Project project, + string language, string directory, string name) + { + bool addedSomething = false; + directory = Path.Combine (directory, dirName); + if (templates.Count == 0) { + string relPath = FileService.AbsoluteToRelativePath (project.BaseDirectory, directory); + if (project.AddDirectory (relPath) != null) + addedSomething = true; + } + + foreach (FileDescriptionTemplate t in templates) { if (t.EvaluateCreateCondition ()) addedSomething |= t.AddToProject (policyParent, project, language, directory, name); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/FileDescriptionTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/FileDescriptionTemplate.cs index 4d6b1fe1ba..2164ddc98a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/FileDescriptionTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/FileDescriptionTemplate.cs @@ -37,6 +37,7 @@ using Mono.Addins; using MonoDevelop.Projects; using MonoDevelop.Ide.Codons; using MonoDevelop.Core.StringParsing; +using System.Threading.Tasks; namespace MonoDevelop.Ide.Templates { @@ -73,7 +74,17 @@ namespace MonoDevelop.Ide.Templates public abstract string Name { get; } public abstract void Load (XmlElement filenode, FilePath baseDirectory); - public abstract bool AddToProject (SolutionFolderItem policyParent, Project project, string language, string directory, string name); + + [Obsolete("Use public abstract Task<bool> AddToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string name)")] + public virtual bool AddToProject (SolutionFolderItem policyParent, Project project, string language, string directory, string name) + { + return false; + } + + public virtual Task<bool> AddToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string name) + { + return Task.FromResult(AddToProject (policyParent, project, language, directory, name)); + } public abstract void Show (); internal string CreateCondition { get; private set; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/FileTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/FileTemplate.cs index b183a357c2..8777fe313a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/FileTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/FileTemplate.cs @@ -34,6 +34,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using System.Xml; using Gtk; using Mono.Addins; @@ -244,13 +245,13 @@ namespace MonoDevelop.Ide.Templates return node == null ? null : LoadTemplate (node); } - public virtual bool Create (SolutionFolderItem policyParent, Project project, string directory, string language, string name) + public virtual async Task<bool> Create (SolutionFolderItem policyParent, Project project, string directory, string language, string name) { if (!String.IsNullOrEmpty (WizardPath)) { return false; } else { foreach (FileDescriptionTemplate newfile in Files) - if (!CreateFile (newfile, policyParent, project, directory, language, name)) + if (!await CreateFile (newfile, policyParent, project, directory, language, name)) return false; return true; } @@ -303,13 +304,13 @@ namespace MonoDevelop.Ide.Templates } } - protected virtual bool CreateFile (FileDescriptionTemplate newfile, SolutionFolderItem policyParent, Project project, string directory, string language, string name) + protected virtual async Task<bool> CreateFile (FileDescriptionTemplate newfile, SolutionFolderItem policyParent, Project project, string directory, string language, string name) { if (project != null) { var model = project.GetStringTagModel (new DefaultConfigurationSelector ()); newfile.SetProjectTagModel (model); try { - if (newfile.AddToProject (policyParent, project, language, directory, name)) { + if (await newfile.AddToProjectAsync (policyParent, project, language, directory, name)) { newfile.Show (); return true; } @@ -322,14 +323,14 @@ namespace MonoDevelop.Ide.Templates throw new InvalidOperationException ("Single file template expected"); if (directory != null) { - string fileName = singleFile.SaveFile (policyParent, project, language, directory, name); + string fileName = await singleFile.SaveFileAsync (policyParent, project, language, directory, name); if (fileName != null) { IdeApp.Workbench.OpenDocument (fileName, project); return true; } } else { string fileName = singleFile.GetFileName (policyParent, project, language, directory, name); - Stream stream = singleFile.CreateFileContent (policyParent, project, language, fileName, name); + Stream stream = singleFile.CreateFileContent (policyParent, project, language, fileName, name) ?? await singleFile.CreateFileContentAsync (policyParent, project, language, fileName, name); string mimeType = GuessMimeType (fileName); IdeApp.Workbench.NewDocument (fileName, mimeType, stream); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/FileTemplateReference.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/FileTemplateReference.cs index f815ec1c8c..9a6e891791 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/FileTemplateReference.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/FileTemplateReference.cs @@ -34,6 +34,7 @@ using System.Xml; using MonoDevelop.Projects; using MonoDevelop.Core; using MonoDevelop.Core.StringParsing; +using System.Threading.Tasks; namespace MonoDevelop.Ide.Templates { @@ -67,7 +68,7 @@ namespace MonoDevelop.Ide.Templates get { return name;} } - public override bool AddToProject (SolutionFolderItem policyParent, Project project, string language, string directory, string entryName) + public override async Task<bool> AddToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string entryName) { string[,] customTags = new string[,] { {"ProjectName", project.Name}, @@ -79,6 +80,26 @@ namespace MonoDevelop.Ide.Templates foreach (FileDescriptionTemplate fdt in innerTemplate.Files) { if (fdt.EvaluateCreateCondition ()) { + if (!await fdt.AddToProjectAsync (policyParent, project, language, directory, substName)) + return false; + } + } + return true; + } + + [Obsolete ("Use public Task<bool> AddToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string entryName).")] + public override bool AddToProject (SolutionFolderItem policyParent, Project project, string language, string directory, string entryName) + { + string[,] customTags = new string[,] { + {"ProjectName", project.Name}, + {"EntryName", entryName}, + {"EscapedProjectName", GetDotNetIdentifier (project.Name) } + }; + + string substName = StringParserService.Parse (this.name, customTags); + + foreach (FileDescriptionTemplate fdt in innerTemplate.Files) { + if (fdt.EvaluateCreateCondition ()) { if (!fdt.AddToProject (policyParent, project, language, directory, substName)) return false; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ItemTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ItemTemplate.cs new file mode 100644 index 0000000000..ab18ac9ef6 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ItemTemplate.cs @@ -0,0 +1,49 @@ +ο»Ώ// +// ItemTemplate.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.IO; + +namespace MonoDevelop.Ide.Templates +{ + public class ItemTemplate + { + internal ItemTemplate (string id, string name) + { + Id = id; + Name = name; + } + + public string Id { get; private set; } + public string Name { get; private set; } + public string Language { get; set; } + + public virtual Stream GetStream (string path) + { + return null; + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ItemTemplatePackageInstaller.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ItemTemplatePackageInstaller.cs new file mode 100644 index 0000000000..141bf9698d --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ItemTemplatePackageInstaller.cs @@ -0,0 +1,37 @@ +ο»Ώ// +// ItemTemplatePackageInstaller.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.Collections.Generic;
+using System.Threading.Tasks; +using MonoDevelop.Projects; + +namespace MonoDevelop.Ide.Templates +{ + public abstract class ItemTemplatePackageInstaller + { + public abstract Task Run (Project project, IEnumerable<TemplatePackageReference> packageReferences); + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngine.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngine.cs new file mode 100644 index 0000000000..156114a3fd --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngine.cs @@ -0,0 +1,349 @@ +// +// MicrosoftTemplateEngine.cs +// +// Author: +// David KarlaΕ‘ <david.karlas@xamarin.com> +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System;
+using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.TemplateEngine.Abstractions; +using Microsoft.TemplateEngine.Abstractions.Mount; +using Microsoft.TemplateEngine.Edge; +using Microsoft.TemplateEngine.Edge.Settings; +using Microsoft.TemplateEngine.Edge.Template;
+using Microsoft.TemplateEngine.Orchestrator.RunnableProjects; +using Microsoft.TemplateEngine.Orchestrator.RunnableProjects.Config; +using Microsoft.TemplateEngine.Orchestrator.RunnableProjects.Macros; +using Microsoft.TemplateEngine.Utils; +using Mono.Addins; +using MonoDevelop.Core; +using MonoDevelop.Core.Text; +using MonoDevelop.Ide.CodeFormatting; +using MonoDevelop.Ide.Codons; +using MonoDevelop.Ide.Projects; +using MonoDevelop.Projects.Policies; + +namespace MonoDevelop.Ide.Templates +{ + class MicrosoftTemplateEngine + { + static EngineEnvironmentSettings environmentSettings = new EngineEnvironmentSettings (new MyTemplateEngineHost (), (env) => new SettingsLoader (env)); + static TemplateCreator templateCreator = new TemplateCreator (environmentSettings); + + static bool dontUpdateCache = true; + + static MicrosoftTemplateEngine () + { + AddinManager.AddExtensionNodeHandler ("/MonoDevelop/Ide/Templates", OnProjectTemplateExtensionChanged); + AddinManager.AddExtensionNodeHandler ("/MonoDevelop/Ide/ItemTemplates", OnItemTemplateExtensionChanged); + dontUpdateCache = false; + UpdateCache (); + } + + static List<TemplateExtensionNode> projectTemplateNodes = new List<TemplateExtensionNode> (); + static List<MicrosoftTemplateEngineSolutionTemplate> projectTemplates = new List<MicrosoftTemplateEngineSolutionTemplate> (); + static List<ItemTemplateExtensionNode> itemTemplateNodes = new List<ItemTemplateExtensionNode> (); + static List<MicrosoftTemplateEngineItemTemplate> itemTemplates = new List<MicrosoftTemplateEngineItemTemplate> (); + + static void UpdateCache () + { + if (dontUpdateCache)//Avoid updating cache while scan paths are added during registration + return; + + // Prevent a TypeInitializationException in when calling SettingsLoader.Save when no templates + // are available, which throws an exception, by returning here. This prevents the MonoDevelop.Ide addin + // from loading. In practice this should not happen unless the .NET Core addin is disabled. + if (!projectTemplateNodes.Any () && !itemTemplateNodes.Any ()) + return; + + var paths = new Paths (environmentSettings); + + //TODO: Uncomment this IF, but also add logic to invalidate/check if new templates were added from newly installed AddOns... + //if (!paths.Exists (paths.User.BaseDir) || !paths.Exists (paths.User.FirstRunCookie)) { + paths.DeleteDirectory (paths.User.BaseDir);//Delete cache + var settingsLoader = (SettingsLoader)environmentSettings.SettingsLoader; + + foreach (var scanPath in projectTemplateNodes.Select (t => t.ScanPath).Distinct ()) { + settingsLoader.UserTemplateCache.Scan (scanPath); + } + + foreach (var scanPath in itemTemplateNodes.Select (t => t.ScanPath).Distinct ()) { + settingsLoader.UserTemplateCache.Scan (scanPath); + } + + settingsLoader.Save (); + paths.WriteAllText (paths.User.FirstRunCookie, ""); + //} + var templateInfos = settingsLoader.UserTemplateCache.List (false, t => new MatchInfo ()).ToDictionary (m => m.Info.Identity, m => m.Info); + var newProjectTemplates = new List<MicrosoftTemplateEngineSolutionTemplate> (); + foreach (var template in projectTemplateNodes) { + ITemplateInfo templateInfo; + if (!templateInfos.TryGetValue (template.TemplateId, out templateInfo)) { + LoggingService.LogWarning ("Template {0} not found.", template.TemplateId); + continue; + } + newProjectTemplates.Add (new MicrosoftTemplateEngineSolutionTemplate (template, templateInfo)); + } + projectTemplates = newProjectTemplates; + + var newItemTemplates = new List<MicrosoftTemplateEngineItemTemplate> (); + foreach (var template in itemTemplateNodes) { + ITemplateInfo templateInfo; + if (!templateInfos.TryGetValue (template.TemplateId, out templateInfo)) { + LoggingService.LogWarning ("Template {0} not found.", template.TemplateId); + continue; + } + newItemTemplates.Add (new MicrosoftTemplateEngineItemTemplate (template, templateInfo)); + } + itemTemplates = newItemTemplates; + } + + static void OnProjectTemplateExtensionChanged (object sender, ExtensionNodeEventArgs args) + { + var node = (TemplateExtensionNode)args.ExtensionNode; + + OnExtensionChanged (projectTemplateNodes, node, args.Change); + } + + static void OnItemTemplateExtensionChanged (object sender, ExtensionNodeEventArgs args) + { + var node = (ItemTemplateExtensionNode)args.ExtensionNode; + + OnExtensionChanged (itemTemplateNodes, node, args.Change); + } + + static void OnExtensionChanged<T> (List<T> extensionNodes, T node, ExtensionChange change) + where T : ExtensionNode + { + if (change == ExtensionChange.Add) { + try { + extensionNodes.Add (node); + } catch (Exception ex) { + LogExtensionChangedError (ex, node); + } + } else { + foreach (var existingNode in extensionNodes) { + if (existingNode.Id == node.Id) { + extensionNodes.Remove (existingNode); + break; + } + } + } + + UpdateCache (); + } + + static void LogExtensionChangedError (Exception ex, ExtensionNode node) + { + string extId = null; + string addinId = null; + + if (node != null) { + if (node.HasId) + extId = node.Id; + if (node.Addin != null) + addinId = node.Addin.Id; + } + + LoggingService.LogError ("Error loading template id {0} in addin {1}:\n{2}", + extId ?? "(null)", addinId ?? "(null)", ex.ToString ()); + } + + public static IEnumerable<SolutionTemplate> GetProjectTemplates () + { + return projectTemplates; + } + + public static IEnumerable<ItemTemplate> GetItemTemplates () + { + return itemTemplates; + } + + /// <summary> + /// Used by unit tests to create a new solution template without having to use an addin. + /// </summary> + static internal SolutionTemplate CreateProjectTemplate (string templateId, string scanPath) + { + var settingsLoader = (SettingsLoader)environmentSettings.SettingsLoader; + settingsLoader.UserTemplateCache.Scan (scanPath); + settingsLoader.Save (); + + var templateInfo = settingsLoader.UserTemplateCache.TemplateInfo + .FirstOrDefault (t => t.Identity == templateId); + + return new MicrosoftTemplateEngineSolutionTemplate (templateId, templateId, null, templateInfo); + } + + public static Task<TemplateCreationResult> InstantiateAsync ( + ITemplateInfo templateInfo, + NewProjectConfiguration config, + IReadOnlyDictionary<string, string> parameters) + { + return templateCreator.InstantiateAsync ( + templateInfo, + config.ProjectName, + config.GetValidProjectName (), + config.ProjectLocation, + parameters, + true, + false, + null + ); + } + + public static Task<TemplateCreationResult> InstantiateAsync ( + ITemplateInfo templateInfo, + NewItemConfiguration config, + IReadOnlyDictionary<string, string> parameters) + { + return templateCreator.InstantiateAsync ( + templateInfo, + config.NameWithoutExtension, + config.NameWithoutExtension, + config.Directory, + parameters, + true, + false, + null + ); + } + + public static string GetPath (ICreationPath path) + { + return NormalizePath (path.Path); + } + + static string NormalizePath (string path) + { + if (Path.DirectorySeparatorChar != '\\') + return path.Replace ('\\', Path.DirectorySeparatorChar); + + return path; + } + + public static async Task FormatFile (PolicyContainer policies, FilePath file) + { + string mime = DesktopService.GetMimeTypeForUri (file); + if (mime == null) + return; + + var formatter = CodeFormatterService.GetFormatter (mime); + if (formatter != null) { + try { + var content = await TextFileUtility.ReadAllTextAsync (file); + var formatted = formatter.FormatText (policies, content.Text); + if (formatted != null) + TextFileUtility.WriteText (file, formatted, content.Encoding); + } catch (Exception ex) { + LoggingService.LogError ("File formatting failed", ex); + } + } + } + + public static string MergeDefaultParameters (string defaultParameters, ITemplateInfo templateInfo) + { + List<TemplateParameter> priorityParameters = null; + var parameters = new List<string> (); + var cacheParameters = templateInfo.CacheParameters.Where (m => !string.IsNullOrEmpty (m.Value.DefaultValue)); + + if (!cacheParameters.Any ()) + return defaultParameters; + + if (!string.IsNullOrEmpty (defaultParameters)) { + priorityParameters = TemplateParameter.CreateParameters (defaultParameters).ToList (); + defaultParameters += ","; + } + + foreach (var p in cacheParameters) { + if (priorityParameters == null || !priorityParameters.Exists (t => t.Name == p.Key)) + parameters.Add ($"{p.Key}={p.Value.DefaultValue}"); + } + + return defaultParameters += string.Join (",", parameters); + } + + public static string GetLanguage (ITemplateInfo templateInfo) + { + ICacheTag languageTag; + if (templateInfo.Tags.TryGetValue ("language", out languageTag)) { + return languageTag.DefaultValue; + } + + return string.Empty; + } + + /// <summary> + /// Use '${TemplateConfigDirectory}/template.json' to get the template.json file + /// without having to specify the full path. + /// </summary> + public static Stream GetStream (ITemplateInfo template, string path) + { + path = NormalizePath (template, path); + + var settingsLoader = (SettingsLoader)environmentSettings.SettingsLoader; + + IMountPoint mountPoint; + IFile file; + if (settingsLoader.TryGetFileFromIdAndPath (template.ConfigMountPointId, path, out file, out mountPoint)) { + return file.OpenRead (); + } + + return null; + } + + static string NormalizePath (ITemplateInfo template, string path) + { + path = NormalizePath (path); + + var tags = new string[,] { + {"TemplateConfigDirectory", Path.GetDirectoryName (template.ConfigPlace) } + }; + + return StringParserService.Parse (path, tags); + } + + class MyTemplateEngineHost : DefaultTemplateEngineHost + { + static readonly AssemblyComponentCatalog builtIns = new AssemblyComponentCatalog (new[] { + typeof (RunnableProjectGenerator).Assembly, + }); + + public MyTemplateEngineHost () : base (BrandingService.ApplicationName, BuildInfo.CompatVersion, "en-US", new Dictionary<string, string> { { "dotnet-cli-version", "0" } }, builtIns) + { + } + + public override bool TryGetHostParamDefault (string paramName, out string value) + { + if (paramName == "HostIdentifier") { + value = this.HostIdentifier; + return true; + } + value = null; + return false; + } + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineItemTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineItemTemplate.cs new file mode 100644 index 0000000000..1dc550c4ee --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineItemTemplate.cs @@ -0,0 +1,50 @@ +ο»Ώ// +// MicrosoftTemplateEngineItemTemplate.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System.IO; +using Microsoft.TemplateEngine.Abstractions; +using MonoDevelop.Ide.Codons; + +namespace MonoDevelop.Ide.Templates +{ + class MicrosoftTemplateEngineItemTemplate : ItemTemplate + { + public MicrosoftTemplateEngineItemTemplate (ItemTemplateExtensionNode template, ITemplateInfo templateInfo) + : base (template.TemplateId, template.OverrideName ?? templateInfo.Name) + { + TemplateInfo = templateInfo; + + Language = MicrosoftTemplateEngine.GetLanguage (templateInfo); + } + + internal ITemplateInfo TemplateInfo { get; private set; } + + public override Stream GetStream (string path) + { + return MicrosoftTemplateEngine.GetStream (TemplateInfo, path); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineItemTemplatingProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineItemTemplatingProvider.cs new file mode 100644 index 0000000000..647cbd7c48 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineItemTemplatingProvider.cs @@ -0,0 +1,166 @@ +ο»Ώ// +// MicrosoftTemplateEngineItemTemplatingProvider.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Microsoft.TemplateEngine.Abstractions; +using Microsoft.TemplateEngine.Edge.Template; +using Mono.Addins; +using MonoDevelop.Core.StringParsing; +using MonoDevelop.Ide.Codons; +using MonoDevelop.Projects; +using System.Linq; +using MonoDevelop.Core; +using MonoDevelop.Projects.Policies; +using MonoDevelop.Ide.CodeFormatting; +using MonoDevelop.Core.Text; + +namespace MonoDevelop.Ide.Templates +{ + class MicrosoftTemplateEngineItemTemplatingProvider + { + public IEnumerable<ItemTemplate> GetTemplates () + { + return MicrosoftTemplateEngine.GetItemTemplates (); + } + + public async Task ProcessTemplate (ItemTemplate template, Project project, NewItemConfiguration config) + { + var itemTemplate = (MicrosoftTemplateEngineItemTemplate)template; + var parameters = GetParameters (project, itemTemplate, config); + var result = await MicrosoftTemplateEngine.InstantiateAsync (itemTemplate.TemplateInfo, config, parameters); + + if (result.Status != CreationResultStatus.Success) { + string message = string.Format ("Could not create template. Id='{0}' {1} {2}", template.Id, result.Status, result.Message); + throw new InvalidOperationException (message); + } + + foreach (var path in result.ResultInfo.PrimaryOutputs) { + string fullPath = Path.Combine (config.Directory, GetPath (path)); + + await MicrosoftTemplateEngine.FormatFile (project?.Policies, fullPath); + + if (project != null) { + AddFileToProject (project, fullPath); + } + + IdeApp.Workbench.OpenDocument (fullPath, project).Ignore (); + + if (project != null) { + await InstallNuGetPackages (project, result.ResultInfo); + } + } + } + + static Dictionary<string, string> GetParameters (Project project, MicrosoftTemplateEngineItemTemplate template, NewItemConfiguration config) + { + var parameters = new Dictionary<string, string> (); + + var model = (IStringTagModel)config; + foreach (ITemplateParameter parameter in template.TemplateInfo.Parameters) { + string parameterValue = (string)model.GetValue (parameter.Name); + if (parameterValue != null) + parameters [parameter.Name] = parameterValue; + } + + var dotNetProject = project as DotNetProject; + if (dotNetProject != null) { + string fileName = GetFullPathIncludingFileExtension (template, config); + parameters ["namespace"] = dotNetProject.GetDefaultNamespace (fileName); + } + + return parameters; + } + + static string GetFullPathIncludingFileExtension (MicrosoftTemplateEngineItemTemplate template, NewItemConfiguration config) + { + string fileName = config.Name; + if (StringComparer.OrdinalIgnoreCase.Equals (template.Language, "C#")) { + fileName = Path.ChangeExtension (config.Name, ".cs"); + } + return Path.Combine (config.Directory, fileName); + } + + void AddFileToProject (Project project, string fileName) + { + string buildAction = project.GetDefaultBuildAction (fileName); + ProjectFile projectFile = project.AddFile (fileName, buildAction); + } + + static string GetPath (ICreationPath path) + { + return MicrosoftTemplateEngine.GetPath (path); + } + + async Task InstallNuGetPackages (Project project, ICreationResult result) + { + var packageReferences = GetPackageReferences (result).ToList (); + + if (!packageReferences.Any ()) + return; + + foreach (ItemTemplatePackageInstaller installer in AddinManager.GetExtensionObjects ("/MonoDevelop/Ide/ItemTemplatePackageInstallers")) { + await installer.Run (project, packageReferences); + } + } + + IEnumerable<TemplatePackageReference> GetPackageReferences (ICreationResult result) + { + foreach (var postAction in result.PostActions) { + var packageReference = CreatePackageReference (postAction); + if (packageReference != null) { + yield return packageReference; + } + } + } + + TemplatePackageReference CreatePackageReference (IPostAction action) + { + if (!IsInstallPackagePostAction (action)) + return null; + + if (!action.Args.TryGetValue ("reference", out string packageId)) + return null; + + if (!action.Args.TryGetValue ("version", out string packageVersion)) + return null; + + return new TemplatePackageReference (packageId, packageVersion); + } + + static readonly Guid addReferencePostActionId = new Guid ("B17581D1-C5C9-4489-8F0A-004BE667B814"); + + static bool IsInstallPackagePostAction (IPostAction action) + { + return action.ActionId == addReferencePostActionId && + action.Args != null && + action.Args.TryGetValue ("referenceType", out string referenceType) && + StringComparer.OrdinalIgnoreCase.Equals (referenceType, "package"); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineProjectTemplatingProvider.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineProjectTemplatingProvider.cs index 4fa481ba91..6724b41f39 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineProjectTemplatingProvider.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineProjectTemplatingProvider.cs @@ -1,4 +1,4 @@ -ο»Ώ// +// // MicrosoftTemplateEngineProjectTemplatingProvider.cs // // Author: @@ -55,89 +55,9 @@ namespace MonoDevelop.Ide.Templates return template is MicrosoftTemplateEngineSolutionTemplate; } - static EngineEnvironmentSettings environmentSettings = new EngineEnvironmentSettings (new MyTemplateEngineHost (), (env) => new SettingsLoader (env)); - static TemplateCreator templateCreator = new TemplateCreator (environmentSettings); - - static bool dontUpdateCache = true; - - static MicrosoftTemplateEngineProjectTemplatingProvider () - { - AddinManager.AddExtensionNodeHandler ("/MonoDevelop/Ide/Templates", OnExtensionChanged); - dontUpdateCache = false; - UpdateCache (); - } - - static List<TemplateExtensionNode> TemplatesNodes = new List<TemplateExtensionNode> (); - static List<MicrosoftTemplateEngineSolutionTemplate> templates = new List<MicrosoftTemplateEngineSolutionTemplate> (); - - static void UpdateCache () - { - if (dontUpdateCache)//Avoid updating cache while scan paths are added during registration - return; - - // Prevent a TypeInitializationException in when calling SettingsLoader.Save when no templates - // are available, which throws an exception, by returning here. This prevents the MonoDevelop.Ide addin - // from loading. In practice this should not happen unless the .NET Core addin is disabled. - if (!TemplatesNodes.Any ()) - return; - - var paths = new Paths (environmentSettings); - - //TODO: Uncomment this IF, but also add logic to invalidate/check if new templates were added from newly installed AddOns... - //if (!paths.Exists (paths.User.BaseDir) || !paths.Exists (paths.User.FirstRunCookie)) { - paths.DeleteDirectory (paths.User.BaseDir);//Delete cache - var settingsLoader = (SettingsLoader)environmentSettings.SettingsLoader; - foreach (var scanPath in TemplatesNodes.Select (t => t.ScanPath).Distinct ()) { - settingsLoader.UserTemplateCache.Scan (scanPath); - } - settingsLoader.Save (); - paths.WriteAllText (paths.User.FirstRunCookie, ""); - //} - var templateInfos = settingsLoader.UserTemplateCache.List (false, t => new MatchInfo ()).ToDictionary (m => m.Info.Identity, m => m.Info); - var newTemplates = new List<MicrosoftTemplateEngineSolutionTemplate> (); - foreach (var template in TemplatesNodes) { - ITemplateInfo templateInfo; - if (!templateInfos.TryGetValue (template.TemplateId, out templateInfo)) { - LoggingService.LogWarning ("Template {0} not found.", template.TemplateId); - continue; - } - newTemplates.Add (new MicrosoftTemplateEngineSolutionTemplate (template, templateInfo)); - } - templates = newTemplates; - } - - static void OnExtensionChanged (object s, ExtensionNodeEventArgs args) - { - if (args.Change == ExtensionChange.Add) { - var codon = (TemplateExtensionNode)args.ExtensionNode; - try { - TemplatesNodes.Add (codon); - } catch (Exception e) { - string extId = null, addinId = null; - if (codon != null) { - if (codon.HasId) - extId = codon.Id; - if (codon.Addin != null) - addinId = codon.Addin.Id; - } - LoggingService.LogError ("Error loading template id {0} in addin {1}:\n{2}", - extId ?? "(null)", addinId ?? "(null)", e.ToString ()); - } - } else { - foreach (var pt in TemplatesNodes) { - var codon = (TemplateExtensionNode)args.ExtensionNode; - if (pt.Id == codon.Id) { - TemplatesNodes.Remove (pt); - break; - } - } - } - UpdateCache (); - } - public IEnumerable<SolutionTemplate> GetTemplates () { - return templates; + return MicrosoftTemplateEngine.GetProjectTemplates (); } /// <summary> @@ -145,14 +65,7 @@ namespace MonoDevelop.Ide.Templates /// </summary> static internal SolutionTemplate CreateTemplate (string templateId, string scanPath) { - var settingsLoader = (SettingsLoader)environmentSettings.SettingsLoader; - settingsLoader.UserTemplateCache.Scan (scanPath); - settingsLoader.Save (); - - var templateInfo = settingsLoader.UserTemplateCache.TemplateInfo - .FirstOrDefault (t => t.Identity == templateId); - - return new MicrosoftTemplateEngineSolutionTemplate (templateId, templateId, null, templateInfo); + return MicrosoftTemplateEngine.CreateProjectTemplate (templateId, scanPath); } static MonoDevelop.Core.Instrumentation.Counter TemplateCounter = MonoDevelop.Core.Instrumentation.InstrumentationService.CreateCounter ("Template Instantiated", "Project Model", id: "Core.Template.Instantiated"); @@ -166,16 +79,12 @@ namespace MonoDevelop.Ide.Templates var filesBeforeCreation = Directory.GetFiles (config.ProjectLocation, "*", SearchOption.AllDirectories); - var result = await templateCreator.InstantiateAsync ( - templateInfo, - config.ProjectName, - config.GetValidProjectName (), - config.ProjectLocation, - parameters, - true, - false, - null - ); + var result = await MicrosoftTemplateEngine.InstantiateAsync (templateInfo, config, parameters); + + if (result.Status != CreationResultStatus.Success) { + string message = string.Format ("Could not create template. Id='{0}' {1} {2}", template.Id, result.Status, result.Message); + throw new InvalidOperationException (message); + } var filesToOpen = new List<string> (); foreach (var postAction in result.ResultInfo.PostActions) { @@ -236,7 +145,7 @@ namespace MonoDevelop.Ide.Templates foreach (var file in p.Files) { if (!filesBeforeCreation.Contains ((string)file.FilePath, FilePath.PathComparer)) { //Format only newly created files if (solutionTemplate.ShouldFormatFile (file.FilePath)) { - await FormatFile (parentFolder?.Policies ?? p.Policies, file.FilePath); + await MicrosoftTemplateEngine.FormatFile (parentFolder?.Policies ?? p.Policies, file.FilePath); } } } @@ -245,12 +154,9 @@ namespace MonoDevelop.Ide.Templates return processResult; } - string GetPath (ICreationPath path) + static string GetPath (ICreationPath path) { - if (Path.DirectorySeparatorChar != '\\') - return path.Path.Replace ('\\', Path.DirectorySeparatorChar); - - return path.Path; + return MicrosoftTemplateEngine.GetPath (path); } Dictionary<string, string> GetParameters (MicrosoftTemplateEngineSolutionTemplate template, NewProjectConfiguration config) @@ -280,45 +186,5 @@ namespace MonoDevelop.Ide.Templates return TemplateParameter.CreateParameters (parameters) .Where (parameter => parameter.IsValid); } - - async Task FormatFile (PolicyContainer policies, FilePath file) - { - string mime = DesktopService.GetMimeTypeForUri (file); - if (mime == null) - return; - - var formatter = CodeFormatterService.GetFormatter (mime); - if (formatter != null) { - try { - var content = await TextFileUtility.ReadAllTextAsync (file); - var formatted = formatter.FormatText (policies, content.Text); - if (formatted != null) - TextFileUtility.WriteText (file, formatted, content.Encoding); - } catch (Exception ex) { - LoggingService.LogError ("File formatting failed", ex); - } - } - } - - class MyTemplateEngineHost : DefaultTemplateEngineHost - { - static readonly AssemblyComponentCatalog builtIns = new AssemblyComponentCatalog (new[] { - typeof (RunnableProjectGenerator).Assembly, - }); - - public MyTemplateEngineHost () : base (BrandingService.ApplicationName, BuildInfo.CompatVersion, "en-US", new Dictionary<string, string> { { "dotnet-cli-version", "0" } }, builtIns) - { - } - - public override bool TryGetHostParamDefault (string paramName, out string value) - { - if (paramName == "HostIdentifier") { - value = this.HostIdentifier; - return true; - } - value = null; - return false; - } - } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineSolutionTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineSolutionTemplate.cs index b0d0426f3f..c4194bdf1b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineSolutionTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/MicrosoftTemplateEngineSolutionTemplate.cs @@ -43,18 +43,14 @@ namespace MonoDevelop.Ide.Templates this.templateInfo = templateInfo; Description = ParseDescription (template.OverrideDescription) ?? templateInfo.Description; Category = template.Category; - ICacheTag languageTag; - if (templateInfo.Tags.TryGetValue ("language", out languageTag)) - Language = languageTag.DefaultValue; - else - Language = string.Empty; + Language = MicrosoftTemplateEngine.GetLanguage (templateInfo); GroupId = template.GroupId ?? templateInfo.GroupIdentity; //TODO: Support all this params Condition = template.Condition; //ProjectFileExtension = template.FileExtension; Wizard = template.Wizard; SupportedParameters = template.SupportedParameters; - DefaultParameters = MergeDefaultParameters (template.DefaultParameters); + DefaultParameters = MicrosoftTemplateEngine.MergeDefaultParameters (template.DefaultParameters, templateInfo); ImageId = template.ImageId; FileFormattingExclude = template.FileFormatExclude; //ImageFile = template.ImageFile; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/NewItemConfiguration.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/NewItemConfiguration.cs new file mode 100644 index 0000000000..cdfcd5320b --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/NewItemConfiguration.cs @@ -0,0 +1,77 @@ +ο»Ώ// +// NewItemConfiguration.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System;
+using System.Collections.Generic;
+using System.IO; +using MonoDevelop.Core.StringParsing; +using MonoDevelop.Projects; + +namespace MonoDevelop.Ide.Templates +{ + public class NewItemConfiguration : IStringTagModel + { + Dictionary<string, string> parameters; + + public NewItemConfiguration () + { + parameters = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase); + } + + public string Directory { get; set; } + public string Name { get; set; } + + public string NameWithoutExtension { + get { + if (!string.IsNullOrEmpty (Name)) { + return Path.GetFileNameWithoutExtension (Name); + } + return string.Empty; + } + } + + public string this [string name] { + get { + string result; + if (parameters.TryGetValue (name, out result)) { + return result; + } + return string.Empty; + } + set { + parameters [name] = value; + } + } + + object IStringTagModel.GetValue (string name) + { + string result; + if (parameters.TryGetValue (name, out result)) + return result; + return null; + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ProjectDescriptor.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ProjectDescriptor.cs index b22295fcd5..a33e14a0c0 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ProjectDescriptor.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ProjectDescriptor.cs @@ -141,7 +141,7 @@ namespace MonoDevelop.Ide.Templates return project; } - public void InitializeItem (SolutionFolderItem policyParent, ProjectCreateInformation projectCreateInformation, string defaultLanguage, SolutionItem item) + public async void InitializeItem (SolutionFolderItem policyParent, ProjectCreateInformation projectCreateInformation, string defaultLanguage, SolutionItem item) { MonoDevelop.Projects.Project project = item as MonoDevelop.Projects.Project; @@ -177,7 +177,7 @@ namespace MonoDevelop.Ide.Templates try { if (!projectCreateInformation.ShouldCreate (resourceTemplate.CreateCondition)) continue; - var projectFile = new ProjectFile (resourceTemplate.SaveFile (policyParent, project, defaultLanguage, project.BaseDirectory, null)); + var projectFile = new ProjectFile (await resourceTemplate.SaveFileAsync (policyParent, project, defaultLanguage, project.BaseDirectory, null)); projectFile.BuildAction = BuildAction.EmbeddedResource; project.Files.Add (projectFile); } catch (Exception ex) { @@ -192,7 +192,7 @@ namespace MonoDevelop.Ide.Templates if (!projectCreateInformation.ShouldCreate (fileTemplate.CreateCondition)) continue; fileTemplate.SetProjectTagModel (projectCreateInformation.Parameters); - fileTemplate.AddToProject (policyParent, project, defaultLanguage, project.BaseDirectory, null); + await fileTemplate.AddToProjectAsync (policyParent, project, defaultLanguage, project.BaseDirectory, null); } catch (Exception ex) { if (!IdeApp.IsInitialized) throw; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/PropertyDescriptionTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/PropertyDescriptionTemplate.cs index 75aa833cb9..9b63af22a1 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/PropertyDescriptionTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/PropertyDescriptionTemplate.cs @@ -24,6 +24,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; +using System.Threading.Tasks; using System.Xml; using MonoDevelop.Core; using MonoDevelop.Ide.Templates; @@ -68,6 +69,12 @@ namespace MonoDevelop.Ide.Templates throw new InvalidOperationException ("Property is empty"); } + public override Task<bool> AddToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string name) + { + AddToProject (policyParent, project, language, directory, name); + return Task.FromResult(true); + } + public override bool AddToProject (SolutionFolderItem policyParent, Project project, string language, string directory, string name) { var model = CombinedTagModel.GetTagModel (ProjectTagModel, policyParent, project, language, name, null); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ResourceFileDescriptionTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ResourceFileDescriptionTemplate.cs index b52003c6ca..eb05572766 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ResourceFileDescriptionTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/ResourceFileDescriptionTemplate.cs @@ -30,6 +30,7 @@ using System; using System.Xml; using MonoDevelop.Projects; using MonoDevelop.Core; +using System.Threading.Tasks; namespace MonoDevelop.Ide.Templates { @@ -53,6 +54,7 @@ namespace MonoDevelop.Ide.Templates } } + [Obsolete("Use public Task<ProjectFile> AddFileToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string name).")] public override bool AddToProject (SolutionFolderItem policyParent, Project project, string language, string directory, string name) { ProjectFile file = template.AddFileToProject (policyParent, project, language, directory, name); @@ -63,6 +65,17 @@ namespace MonoDevelop.Ide.Templates else return false; } + + public override async Task<bool> AddToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string name) + { + ProjectFile file = await template.AddFileToProjectAsync (policyParent, project, language, directory, name); + if (file != null) { + file.BuildAction = BuildAction.EmbeddedResource; + return true; + } + else + return false; + } public override void Show () { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/SingleFileDescriptionTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/SingleFileDescriptionTemplate.cs index 250c2b496f..48f4c46c32 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/SingleFileDescriptionTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/SingleFileDescriptionTemplate.cs @@ -43,6 +43,7 @@ using MonoDevelop.Ide.CodeFormatting; using MonoDevelop.Ide.Editor; using MonoDevelop.Projects.SharedAssetsProjects; using MonoDevelop.Core.StringParsing; +using System.Threading.Tasks; namespace MonoDevelop.Ide.Templates { @@ -112,14 +113,14 @@ namespace MonoDevelop.Ide.Templates set { addStandardHeader = value; } } - public sealed override bool AddToProject (SolutionFolderItem policyParent, Project project, string language, string directory, string name) + public sealed override async Task<bool> AddToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string name) { - return AddFileToProject (policyParent, project, language, directory, name) != null; + return await AddFileToProjectAsync (policyParent, project, language, directory, name) != null; } - public ProjectFile AddFileToProject (SolutionFolderItem policyParent, Project project, string language, string directory, string name) + public async Task<ProjectFile> AddFileToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string name) { - generatedFile = SaveFile (policyParent, project, language, directory, name); + generatedFile = await SaveFileAsync (policyParent, project, language, directory, name); if (generatedFile != null) { string buildAction = this.buildAction ?? project.GetDefaultBuildAction (generatedFile); ProjectFile projectFile = project.AddFile (generatedFile, buildAction); @@ -157,6 +158,54 @@ namespace MonoDevelop.Ide.Templates } else return null; } + + [Obsolete ("Use public sealed Task<bool> AddToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string name).")] + public sealed override bool AddToProject (SolutionFolderItem policyParent, Project project, string language, string directory, string name) + { + return AddFileToProject (policyParent, project, language, directory, name) != null; + } + + [Obsolete ("Use public Task<ProjectFile> AddFileToProjectAsync (SolutionFolderItem policyParent, Project project, string language, string directory, string name).")] + public ProjectFile AddFileToProject (SolutionFolderItem policyParent, Project project, string language, string directory, string name) + { + generatedFile = SaveFile (policyParent, project, language, directory, name); + if (generatedFile != null) { + string buildAction = this.buildAction ?? project.GetDefaultBuildAction (generatedFile); + ProjectFile projectFile = project.AddFile (generatedFile, buildAction); + + if (!string.IsNullOrEmpty (dependsOn)) { + var model = CombinedTagModel.GetTagModel (ProjectTagModel, policyParent, project, language, name, generatedFile); + string parsedDepName = StringParserService.Parse (dependsOn, model); + if (projectFile.DependsOn != parsedDepName) + projectFile.DependsOn = parsedDepName; + } + + if (!string.IsNullOrEmpty (customTool)) + projectFile.Generator = customTool; + + if (!string.IsNullOrEmpty (customToolNamespace)) { + var model = CombinedTagModel.GetTagModel (ProjectTagModel, policyParent, project, language, name, generatedFile); + projectFile.CustomToolNamespace = StringParserService.Parse (customToolNamespace, model); + } + + if (!string.IsNullOrEmpty (subType)) + projectFile.ContentType = subType; + + DotNetProject netProject = project as DotNetProject; + if (netProject != null) { + // Add required references + foreach (string aref in references) { + string res = netProject.AssemblyContext.GetAssemblyFullName (aref, netProject.TargetFramework); + res = netProject.AssemblyContext.GetAssemblyNameForVersion (res, netProject.TargetFramework); + if (!ContainsReference (netProject, res)) + netProject.References.Add (ProjectReference.CreateAssemblyReference (aref)); + } + } + + return projectFile; + } else + return null; + } public override bool SupportsProject (Project project, string projectPath) { @@ -202,7 +251,7 @@ namespace MonoDevelop.Ide.Templates // Creates a file and saves it to disk. Returns the path to the new file // All parameters are optional (can be null) - public string SaveFile (SolutionFolderItem policyParent, Project project, string language, string baseDirectory, string entryName) + public async Task<string> SaveFileAsync (SolutionFolderItem policyParent, Project project, string language, string baseDirectory, string entryName) { string file = GetFileName (policyParent, project, language, baseDirectory, entryName); AlertButton questionResult = null; @@ -221,6 +270,63 @@ namespace MonoDevelop.Ide.Templates Directory.CreateDirectory (Path.GetDirectoryName (file)); if (questionResult == null || questionResult == AlertButton.OverwriteFile) { + Stream stream = CreateFileContentFromDerivedClass (policyParent, project, language, file, entryName) ?? await CreateFileContentAsync (policyParent, project, language, file, entryName); + + byte [] buffer = new byte [2048]; + int nr; + FileStream fs = null; + try { + fs = File.Create (file); + while ((nr = stream.Read (buffer, 0, 2048)) > 0) + fs.Write (buffer, 0, nr); + } finally { + stream.Close (); + if (fs != null) + fs.Close (); + } + } + return file; + } + + bool createFileContentFromDerivedClass; + + /// <summary> + /// Allows a derived class's CreateFileContent to be called. Need to ensure that if the derived + /// class does not implement CreateFileContent then the SingleFileDescriptionTemplate's + /// CreateFileContentAsync is used instead of CreateFileContent. + /// </summary> + Stream CreateFileContentFromDerivedClass (SolutionFolderItem policyParent, Project project, string language, string fileName, string identifier) + { + createFileContentFromDerivedClass = true; + try { + return CreateFileContent (policyParent, project, language, fileName, identifier); + } finally { + createFileContentFromDerivedClass = false; + } + } + + // Creates a file and saves it to disk. Returns the path to the new file + // All parameters are optional (can be null) + [Obsolete ("Use public Task<string> SaveFileAsync (SolutionFolderItem policyParent, Project project, string language, string baseDirectory, string entryName).")] + public string SaveFile (SolutionFolderItem policyParent, Project project, string language, string baseDirectory, string entryName) + { + string file = GetFileName (policyParent, project, language, baseDirectory, entryName); + AlertButton questionResult = null; + + if (File.Exists (file)) { + questionResult = MessageService.AskQuestion (GettextCatalog.GetString ("File already exists"), + GettextCatalog.GetString ("File {0} already exists.\nDo you want to overwrite the existing file or add it to the project?", file), + AlertButton.Cancel, + AlertButton.AddExistingFile, + AlertButton.OverwriteFile); + if (questionResult == AlertButton.Cancel) + return null; + } + + if (!Directory.Exists (Path.GetDirectoryName (file))) + Directory.CreateDirectory (Path.GetDirectoryName (file)); + + if (questionResult == null || questionResult == AlertButton.OverwriteFile) { Stream stream = CreateFileContent (policyParent, project, language, file, entryName); byte [] buffer = new byte [2048]; @@ -281,8 +387,12 @@ namespace MonoDevelop.Ide.Templates // Returns a stream with the content of the file. // project and language parameters are optional + [Obsolete ("Use public virtual async Task<Stream> CreateFileContentAsync (SolutionFolderItem policyParent, Project project, string language, string fileName, string identifier).")] public virtual Stream CreateFileContent (SolutionFolderItem policyParent, Project project, string language, string fileName, string identifier) { + if (createFileContentFromDerivedClass) + return null; + var model = CombinedTagModel.GetTagModel (ProjectTagModel, policyParent, project, language, identifier, fileName); //HACK: for API compat, CreateContent just gets the override, not the base model @@ -293,13 +403,13 @@ namespace MonoDevelop.Ide.Templates string mime = DesktopService.GetMimeTypeForUri (fileName); var formatter = !string.IsNullOrEmpty (mime) ? CodeFormatterService.GetFormatter (mime) : null; - + if (formatter != null) { var formatted = formatter.FormatText (policyParent != null ? policyParent.Policies : null, content); if (formatted != null) content = formatted; } - + var ms = new MemoryStream (); var bom = Encoding.UTF8.GetPreamble (); @@ -311,17 +421,17 @@ namespace MonoDevelop.Ide.Templates data = System.Text.Encoding.UTF8.GetBytes (header); ms.Write (data, 0, data.Length); } - + var doc = TextEditorFactory.CreateNewDocument (); doc.Text = content; - + TextStylePolicy textPolicy = policyParent != null ? policyParent.Policies.Get<TextStylePolicy> (mime ?? "text/plain") : MonoDevelop.Projects.Policies.PolicyService.GetDefaultPolicy<TextStylePolicy> (mime ?? "text/plain"); string eolMarker = TextStylePolicy.GetEolMarker (textPolicy.EolMarker); byte[] eolMarkerBytes = System.Text.Encoding.UTF8.GetBytes (eolMarker); - + var tabToSpaces = textPolicy.TabsToSpaces? new string (' ', textPolicy.TabWidth) : null; - + foreach (var line in doc.GetLines ()) { var lineText = doc.GetTextAt (line.Offset, line.Length); if (tabToSpaces != null) @@ -332,7 +442,83 @@ namespace MonoDevelop.Ide.Templates ms.Write (eolMarkerBytes, 0, eolMarkerBytes.Length); } } + + ms.Position = 0; + return ms; + } + + // Returns a stream with the content of the file. + // project and language parameters are optional + public virtual async Task<Stream> CreateFileContentAsync (SolutionFolderItem policyParent, Project project, string language, string fileName, string identifier) + { + var model = CombinedTagModel.GetTagModel (ProjectTagModel, policyParent, project, language, identifier, fileName); + + //HACK: for API compat, CreateContent just gets the override, not the base model + // but ProcessContent gets the entire model + string content = CreateContent (project, model.OverrideTags, language); + + content = ProcessContent (content, model); + + string mime = DesktopService.GetMimeTypeForUri (fileName); + var formatter = !string.IsNullOrEmpty (mime) ? CodeFormatterService.GetFormatter (mime) : null; + + if (formatter != null) { + var formatted = formatter.FormatText (policyParent != null ? policyParent.Policies : null, content); + if (formatted != null) + content = formatted; + } + + var ms = new MemoryStream (); + Encoding encoding = null; + TextStylePolicy textPolicy = policyParent != null ? policyParent.Policies.Get<TextStylePolicy> (mime ?? "text/plain") + : MonoDevelop.Projects.Policies.PolicyService.GetDefaultPolicy<TextStylePolicy> (mime ?? "text/plain"); + string eolMarker = TextStylePolicy.GetEolMarker (textPolicy.EolMarker); + + var ctx = await EditorConfigService.GetEditorConfigContext (fileName); + if (ctx != null) { + ctx.CurrentConventions.UniversalConventions.TryGetEncoding (out encoding); + if (ctx.CurrentConventions.UniversalConventions.TryGetLineEnding (out string lineEnding)) + eolMarker = lineEnding; + } + if (encoding == null) + encoding = System.Text.Encoding.UTF8; + var bom = encoding.GetPreamble (); + if (bom != null && bom.Length > 0) + ms.Write (bom, 0, bom.Length); + + byte[] data; + if (AddStandardHeader) { + string header = StandardHeaderService.GetHeader (policyParent, fileName, true); + data = encoding.GetBytes (header); + ms.Write (data, 0, data.Length); + } + + var doc = TextEditorFactory.CreateNewDocument (); + doc.Text = content; + + byte[] eolMarkerBytes = encoding.GetBytes (eolMarker); + + var tabToSpaces = textPolicy.TabsToSpaces? new string (' ', textPolicy.TabWidth) : null; + IDocumentLine lastLine = null; + foreach (var line in doc.GetLines ()) { + var lineText = doc.GetTextAt (line.Offset, line.Length); + if (tabToSpaces != null) + lineText = lineText.Replace ("\t", tabToSpaces); + if (line.LengthIncludingDelimiter > 0) { + data = encoding.GetBytes (lineText); + ms.Write (data, 0, data.Length); + ms.Write (eolMarkerBytes, 0, eolMarkerBytes.Length); + } + lastLine = line; + } + if (ctx != null && lastLine != null && lastLine.Length > 0) { + if (ctx.CurrentConventions.UniversalConventions.TryGetRequireFinalNewline (out bool requireNewLine)) { + if (requireNewLine) + ms.Write (eolMarkerBytes, 0, eolMarkerBytes.Length); + } + } + ms.Position = 0; return ms; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplatePackageReference.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplatePackageReference.cs new file mode 100644 index 0000000000..ba1f28bcee --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplatePackageReference.cs @@ -0,0 +1,40 @@ +ο»Ώ// +// TemplatePackageReference.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2017 Xamarin Inc. (http://xamarin.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +namespace MonoDevelop.Ide.Templates +{ + public class TemplatePackageReference + { + public TemplatePackageReference (string id, string version) + { + Id = id; + Version = version; + } + + public string Id { get; private set; } + public string Version { get; private set; } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplatingService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplatingService.cs index 5edc53bf20..695a3cb04e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplatingService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TemplatingService.cs @@ -46,9 +46,12 @@ namespace MonoDevelop.Ide.Templates List<TemplateWizard> projectTemplateWizards = new List<TemplateWizard> ();
List<ImageCodon> projectTemplateImages = new List<ImageCodon> ();
+ MicrosoftTemplateEngineItemTemplatingProvider itemTemplatingProvider;
+
public TemplatingService ()
{
RecentTemplates = new RecentTemplates ();
+ itemTemplatingProvider = new MicrosoftTemplateEngineItemTemplatingProvider ();
AddinManager.AddExtensionNodeHandler ("/MonoDevelop/Ide/ProjectTemplateCategories", OnTemplateCategoriesChanged);
AddinManager.AddExtensionNodeHandler ("/MonoDevelop/Ide/ProjectTemplatingProviders", OnTemplatingProvidersChanged);
AddinManager.AddExtensionNodeHandler ("/MonoDevelop/Ide/ProjectTemplateWizards", OnProjectTemplateWizardsChanged);
@@ -175,6 +178,21 @@ namespace MonoDevelop.Ide.Templates return null;
}
+ public IEnumerable<ItemTemplate> GetItemTemplates ()
+ {
+ return itemTemplatingProvider.GetTemplates ();
+ }
+
+ public IEnumerable<ItemTemplate> GetItemTemplates (Predicate<ItemTemplate> match)
+ {
+ return GetItemTemplates ().Where (template => match (template));
+ }
+
+ public Task ProcessTemplate (ItemTemplate template, Project project, NewItemConfiguration config)
+ {
+ return itemTemplatingProvider.ProcessTemplate (template, project, config);
+ }
+
public RecentTemplates RecentTemplates { get; private set; }
}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TextFileDescriptionTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TextFileDescriptionTemplate.cs index eaf02e1b4a..6d8f22a755 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TextFileDescriptionTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Templates/TextFileDescriptionTemplate.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; using System.Xml; using MonoDevelop.Core; using MonoDevelop.Core.StringParsing; @@ -103,6 +104,13 @@ namespace MonoDevelop.Ide.Templates contentSrcFile = contentSrcFile.ToAbsolute (baseDirectory); } + public override Task<Stream> CreateFileContentAsync (SolutionFolderItem policyParent, Project project, string language, + string fileName, string identifier) + { + return Task.FromResult<Stream>(File.OpenRead (contentSrcFile)); + } + + [Obsolete ("Use public Task<Stream> CreateFileContentAsync (SolutionFolderItem policyParent, Project project, string language, string fileName, string identifier).")] public override Stream CreateFileContent (SolutionFolderItem policyParent, Project project, string language, string fileName, string identifier) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs index 9b25d238c1..808edbab28 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs @@ -106,9 +106,9 @@ namespace MonoDevelop.Ide.TypeSystem if (String.IsNullOrEmpty (str)) return string.Empty; - StringBuilder sb = new StringBuilder (str.Length); + var sb = StringBuilderCache.Allocate (); MarkupUtilities.AppendEscapedString (sb, str, 0, str.Length); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree(sb); } #region Documentation @@ -266,7 +266,7 @@ namespace MonoDevelop.Ide.TypeSystem { if (maxLineLength <= 0) return text; - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); int lineLength = 0; bool inTag = false; bool inAmp = false; @@ -300,14 +300,14 @@ namespace MonoDevelop.Ide.TypeSystem lineLength = 0; } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree(result); } public static string EscapeText (string text) { if (text == null) return null; - StringBuilder result = new StringBuilder (text.Length); + StringBuilder result = StringBuilderCache.Allocate (); foreach (char ch in text) { switch (ch) { case '<': @@ -335,12 +335,12 @@ namespace MonoDevelop.Ide.TypeSystem break; } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public static string UnescapeText (string text) { - var sb = new StringBuilder (text.Length); + var sb = StringBuilderCache.Allocate (); for (int i = 0; i < text.Length; i++) { char ch = text[i]; if (ch == '&') { @@ -370,7 +370,7 @@ namespace MonoDevelop.Ide.TypeSystem sb.Append (ch); } } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } @@ -381,7 +381,7 @@ namespace MonoDevelop.Ide.TypeSystem static string ParseBody (ISymbol member, XmlTextReader xml, string endTagName, DocumentationFormatOptions options) { - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); bool wasWhiteSpace = true; bool appendSpace = false; string listType = "bullet"; @@ -498,7 +498,7 @@ namespace MonoDevelop.Ide.TypeSystem } } end: - return result.ToString ().Trim (); + return StringBuilderCache.ReturnAndFree (result).Trim (); } static string FormatCref (string cref) @@ -514,9 +514,9 @@ namespace MonoDevelop.Ide.TypeSystem return null; System.IO.StringReader reader = new System.IO.StringReader ("<docroot>" + doc + "</docroot>"); XmlTextReader xml = new XmlTextReader (reader); - StringBuilder ret = new StringBuilder (70); - StringBuilder parameterBuilder = new StringBuilder (); - StringBuilder exceptions = new StringBuilder (); + StringBuilder ret = StringBuilderCache.Allocate (); + StringBuilder parameterBuilder = StringBuilderCache.Allocate (); + StringBuilder exceptions = StringBuilderCache.Allocate (); exceptions.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Exceptions:"))); // ret.Append ("<small>"); int paramCount = 0, exceptionCount = 0, summaryEnd = -1; @@ -565,7 +565,7 @@ namespace MonoDevelop.Ide.TypeSystem exceptions.Append ("</b>"); if (options.SmallText) exceptions.Append ("</small>"); - + exceptions.AppendLine (options.FormatBody (ParseBody (member, xml, xml.Name, options))); break; case "returns": @@ -578,7 +578,7 @@ namespace MonoDevelop.Ide.TypeSystem break; case "param": string paramName = xml.GetAttribute ("name") != null ? xml ["name"].Trim () : ""; - + var body = options.FormatBody (ParseBody (member, xml, xml.Name, options)); if (!IsEmptyDocumentation (body)) { paramCount++; @@ -613,34 +613,37 @@ namespace MonoDevelop.Ide.TypeSystem } } } while (xml.Read ()); - - } catch (Exception ex) { - MonoDevelop.Core.LoggingService.LogError (ex.ToString ()); - return EscapeText (doc); - } - if (IsEmptyDocumentation (ret.ToString ()) && IsEmptyDocumentation (parameterBuilder.ToString ())) - return EscapeText (doc); - if (string.IsNullOrEmpty (options.HighlightParameter) && exceptionCount > 0) - ret.Append (exceptions.ToString ()); - - string result = ret.ToString (); - if (summaryEnd < 0) - summaryEnd = result.Length; - if (paramCount > 0) { - var paramSb = new StringBuilder (); - if (result.Length > 0) - paramSb.AppendLine ();/* + if (IsEmptyDocumentation (ret.ToString ()) && IsEmptyDocumentation (parameterBuilder.ToString ())) + return EscapeText (doc); + if (string.IsNullOrEmpty (options.HighlightParameter) && exceptionCount > 0) + ret.Append (exceptions.ToString ()); + + string result = ret.ToString (); + if (summaryEnd < 0) + summaryEnd = result.Length; + if (paramCount > 0) { + var paramSb = StringBuilderCache.Allocate (); + if (result.Length > 0) + paramSb.AppendLine ();/* paramSb.Append ("<small>"); paramSb.AppendLine (options.FormatHeading (GettextCatalog.GetPluralString ("Parameter:", "Parameters:", paramCount))); paramSb.Append ("</small>");*/ - paramSb.Append (parameterBuilder.ToString ()); - result = result.Insert (summaryEnd, paramSb.ToString ()); + paramSb.Append (parameterBuilder.ToString ()); + result = result.Insert (summaryEnd, StringBuilderCache.ReturnAndFree(paramSb)); + } + result = result.Trim (); + if (result.EndsWith (Environment.NewLine + "</small>")) + result = result.Substring (0, result.Length - (Environment.NewLine + "</small>").Length) + "</small>"; + return result; + } catch (Exception ex) { + MonoDevelop.Core.LoggingService.LogError (ex.ToString ()); + return EscapeText (doc); + } finally { + StringBuilderCache.Free (ret); + StringBuilderCache.Free (parameterBuilder); + StringBuilderCache.Free (exceptions); } - result = result.Trim (); - if (result.EndsWith (Environment.NewLine + "</small>")) - result = result.Substring (0, result.Length - (Environment.NewLine + "</small>").Length) + "</small>"; - return result; } #endregion diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IMonoDevelopHostDocument.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IMonoDevelopHostDocument.cs new file mode 100644 index 0000000000..45ee2c8167 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/IMonoDevelopHostDocument.cs @@ -0,0 +1,44 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.VisualStudio.Text;
+
+namespace MonoDevelop.Ide.TypeSystem
+{
+ interface IMonoDevelopHostDocument
+ {
+ /// <summary>
+ /// Updates the text of the document.
+ /// </summary>
+ void UpdateText (SourceText newText);
+ }
+
+ static class MonoDevelopHostDocumentRegistration
+ {
+ internal static void Register (ITextBuffer textBuffer, IMonoDevelopHostDocument hostDocument)
+ {
+ textBuffer?.Properties.AddProperty (typeof (IMonoDevelopHostDocument), hostDocument);
+ }
+
+ internal static void UnRegister(ITextBuffer textBuffer)
+ {
+ textBuffer?.Properties.RemoveProperty (typeof (IMonoDevelopHostDocument));
+ }
+
+ internal static IMonoDevelopHostDocument FromDocument(Document document)
+ {
+ IMonoDevelopHostDocument containedDocument = null;
+ if (document.TryGetText (out SourceText sourceText)) {
+ ITextBuffer textBuffer = sourceText.Container.TryGetTextBuffer ();
+
+ containedDocument = textBuffer?.Properties.GetProperty<IMonoDevelopHostDocument> (typeof (IMonoDevelopHostDocument));
+ }
+
+ return containedDocument;
+ }
+ }
+}
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MarkupUtilities.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MarkupUtilities.cs index 00fee4d6f3..2cc64ee033 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MarkupUtilities.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MarkupUtilities.cs @@ -28,6 +28,7 @@ using System; using System.Text; +using MonoDevelop.Core; namespace MonoDevelop.Ide.TypeSystem { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs index ea90521a02..f5cc57498c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs @@ -51,10 +51,26 @@ namespace MonoDevelop.Ide.TypeSystem // PERF: cache for the solution location. This is needed due to roslyn querying GetStorageLocation a lot of times. internal ConditionalWeakTable<SolutionId, string> storageMap = new ConditionalWeakTable<SolutionId, string> (); - public string GetStorageLocation (Solution solution) + public event EventHandler<PersistentStorageLocationChangingEventArgs> StorageLocationChanging; + + internal void NotifyStorageLocationChanging (SolutionId sol, string path) + { + lock (storageMap) { + if (storageMap.TryGetValue (sol, out string cached) && path == cached) + return; + + StorageLocationChanging?.Invoke (this, new PersistentStorageLocationChangingEventArgs (sol, path, true)); + storageMap.Remove (sol); + storageMap.Add (sol, path); + } + } + + public string TryGetStorageLocation (SolutionId solutionId) { - storageMap.TryGetValue (solution.Id, out var path); - return path; + lock (storageMap) { + storageMap.TryGetValue (solutionId, out var path); + return path; + } } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopSourceTextContainer.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopSourceTextContainer.cs index 2aae5a01ee..6d5e3993c6 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopSourceTextContainer.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopSourceTextContainer.cs @@ -73,32 +73,34 @@ namespace MonoDevelop.Ide.TypeSystem { var handler = TextChanged; if (handler != null) { - lock (replaceLock) { - var oldText = CurrentText; - var changes = new Microsoft.CodeAnalysis.Text.TextChange[e.TextChanges.Count]; - var changeRanges = new TextChangeRange[e.TextChanges.Count];
- for (int i = 0; i < e.TextChanges.Count; ++i) {
- var c = e.TextChanges[i]; - var span = new TextSpan (c.Offset, c.RemovalLength); - changes[i] = new Microsoft.CodeAnalysis.Text.TextChange (span, c.InsertedText.Text); - changeRanges[i] = new TextChangeRange (span, c.InsertionLength); - } - var newText = oldText.WithChanges (changes); - currentText = newText; - try { - handler (this, new Microsoft.CodeAnalysis.Text.TextChangeEventArgs (oldText, newText, changeRanges)); - } catch (ArgumentException ae) { - if (!workspace.TryGetTarget (out var ws)) - return; - if (!editor.TryGetTarget (out var ed)) - return; - LoggingService.LogWarning (ae.Message + " re opening " + ed.FileName + " as roslyn source text."); - ws.InformDocumentClose (Id, ed.FileName); - Dispose (); // 100% ensure that this object is disposed - if (ws.GetDocument (Id) != null) - TypeSystemService.InformDocumentOpen (Id, ed); - } catch (Exception ex) { - LoggingService.LogError ("Error while text replacing", ex); + if (e.TextChanges.Count > 0) {
+ lock (replaceLock) { + var oldText = CurrentText; + var changes = new Microsoft.CodeAnalysis.Text.TextChange[e.TextChanges.Count]; + var changeRanges = new TextChangeRange[e.TextChanges.Count];
+ for (int i = 0; i < e.TextChanges.Count; ++i) {
+ var c = e.TextChanges[i]; + var span = new TextSpan (c.Offset, c.RemovalLength); + changes[i] = new Microsoft.CodeAnalysis.Text.TextChange (span, c.InsertedText.Text); + changeRanges[i] = new TextChangeRange (span, c.InsertionLength); + } + var newText = oldText.WithChanges (changes); + currentText = newText; + try { + handler (this, new Microsoft.CodeAnalysis.Text.TextChangeEventArgs (oldText, newText, changeRanges)); + } catch (ArgumentException ae) { + if (!workspace.TryGetTarget (out var ws)) + return; + if (!editor.TryGetTarget (out var ed)) + return; + LoggingService.LogWarning (ae.Message + " re opening " + ed.FileName + " as roslyn source text."); + ws.InformDocumentClose (Id, ed.FileName); + Dispose (); // 100% ensure that this object is disposed + if (ws.GetDocument (Id) != null) + TypeSystemService.InformDocumentOpen (Id, ed); + } catch (Exception ex) { + LoggingService.LogError ("Error while text replacing", ex); + } } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs index 7dacb46048..1378da5b17 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs @@ -103,14 +103,23 @@ namespace MonoDevelop.Ide.TypeSystem IdeApp.Workspace.ActiveConfigurationChanged += HandleActiveConfigurationChanged;
}
ISolutionCrawlerRegistrationService solutionCrawler = Services.GetService<ISolutionCrawlerRegistrationService> ();
- //Options = Options.WithChangedOption (Microsoft.CodeAnalysis.Diagnostics.InternalRuntimeDiagnosticOptions.Syntax, true)
- // .WithChangedOption (Microsoft.CodeAnalysis.Diagnostics.InternalRuntimeDiagnosticOptions.Semantic, true);
+
+ // Trigger running compiler syntax and semantic errors via the diagnostic analyzer engine
+ Options = Options.WithChangedOption (Microsoft.CodeAnalysis.Diagnostics.InternalRuntimeDiagnosticOptions.Syntax, true)
+ .WithChangedOption (Microsoft.CodeAnalysis.Diagnostics.InternalRuntimeDiagnosticOptions.Semantic, true)
+ // Always use persistent storage regardless of solution size, at least until a consensus is reached
+ // https://github.com/mono/monodevelop/issues/4149 https://github.com/dotnet/roslyn/issues/25453
+ .WithChangedOption (Microsoft.CodeAnalysis.Storage.StorageOptions.SolutionSizeThreshold, 0);
if (IdeApp.Preferences.EnableSourceAnalysis) {
solutionCrawler.Register (this);
}
IdeApp.Preferences.EnableSourceAnalysis.Changed += OnEnableSourceAnalysisChanged;
+
+ foreach (var factory in AddinManager.GetExtensionObjects<Microsoft.CodeAnalysis.Options.IDocumentOptionsProviderFactory>("/MonoDevelop/Ide/TypeService/OptionProviders"))
+ Services.GetRequiredService<Microsoft.CodeAnalysis.Options.IOptionService> ().RegisterDocumentOptionsProvider (factory.Create (this));
+
}
void OnEnableSourceAnalysisChanged(object sender, EventArgs args)
@@ -206,15 +215,12 @@ namespace MonoDevelop.Ide.TypeSystem var mdProjects = solution.GetAllProjects ();
ImmutableList<ProjectionEntry> toDispose;
lock (projectionListUpdateLock) {
- toDispose = projectionList; + toDispose = projectionList;
projectionList = projectionList.Clear ();
}
foreach (var p in toDispose)
p.Dispose ();
- projectIdMap.Clear ();
- projectIdToMdProjectMap = projectIdToMdProjectMap.Clear ();
- projectDataMap.Clear ();
solutionData = new SolutionData ();
List<Task> allTasks = new List<Task> ();
foreach (var proj in mdProjects) {
@@ -223,7 +229,7 @@ namespace MonoDevelop.Ide.TypeSystem var netProj = proj as MonoDevelop.Projects.DotNetProject;
if (netProj != null && !netProj.SupportsRoslyn)
continue;
- var tp = LoadProject (proj, token).ContinueWith (t => {
+ var tp = LoadProject (proj, token, null).ContinueWith (t => {
if (!t.IsCanceled)
projects.Add (t.Result);
});
@@ -245,39 +251,39 @@ namespace MonoDevelop.Ide.TypeSystem lock (addLock) {
if (!added) {
added = true;
- // HACK: https://github.com/dotnet/roslyn/issues/20581 - RegisterPrimarySolutionForPersistentStorage (solutionId, solution); + solution.Modified += OnSolutionModified;
+ NotifySolutionModified (solution, solutionId, this); OnSolutionAdded (solutionInfo);
+ lock (generatedFiles) {
+ foreach (var generatedFile in generatedFiles) {
+ if (!this.IsDocumentOpen (generatedFile.Key.Id))
+ OnDocumentOpened (generatedFile.Key.Id, generatedFile.Value);
+ }
+ }
}
}
return solutionInfo;
});
}
- void RegisterPrimarySolutionForPersistentStorage (SolutionId solutionId, MonoDevelop.Projects.Solution solution) - { - var locService = (MonoDevelopPersistentStorageLocationService)Services.GetService<IPersistentStorageLocationService> (); - locService.storageMap.Add (solutionId, solution.GetPreferencesDirectory ()); - - var service = Services.GetService<IPersistentStorageService> () as Microsoft.CodeAnalysis.Storage.AbstractPersistentStorageService; - if (service == null) { - return; - } - - service.RegisterPrimarySolution (solutionId); - } - - void UnregisterPrimarySolutionForPersistentStorage (SolutionId solutionId, bool synchronousShutdown) - { - var locService = (MonoDevelopPersistentStorageLocationService)Services.GetService<IPersistentStorageLocationService> (); - locService.storageMap.Remove (solutionId); - - var service = Services.GetService<IPersistentStorageService> () as Microsoft.CodeAnalysis.Storage.AbstractPersistentStorageService; - if (service == null) { - return; - } - - service.UnregisterPrimarySolution (solutionId, synchronousShutdown); + static async void OnSolutionModified (object sender, MonoDevelop.Projects.WorkspaceItemEventArgs args)
+ {
+ var sol = (MonoDevelop.Projects.Solution)args.Item;
+ var workspace = await TypeSystemService.GetWorkspaceAsync (sol, CancellationToken.None);
+ var solId = workspace.GetSolutionId (sol);
+ if (solId == null)
+ return;
+
+ NotifySolutionModified (sol, solId, workspace);
+ }
+
+ static void NotifySolutionModified (MonoDevelop.Projects.Solution sol, SolutionId solId, MonoDevelopWorkspace workspace)
+ {
+ if (string.IsNullOrWhiteSpace (sol.BaseDirectory))
+ return;
+
+ var locService = (MonoDevelopPersistentStorageLocationService)workspace.Services.GetService<IPersistentStorageLocationService> ();
+ locService.NotifyStorageLocationChanging (solId, sol.GetPreferencesDirectory ());
} internal Task<SolutionInfo> TryLoadSolution (CancellationToken cancellationToken = default(CancellationToken))
@@ -288,7 +294,6 @@ namespace MonoDevelop.Ide.TypeSystem internal void UnloadSolution ()
{
OnSolutionRemoved ();
- UnregisterPrimarySolutionForPersistentStorage (CurrentSolution.Id, synchronousShutdown: false);
}
Dictionary<MonoDevelop.Projects.Solution, SolutionId> solutionIdMap = new Dictionary<MonoDevelop.Projects.Solution, SolutionId> ();
@@ -448,12 +453,21 @@ namespace MonoDevelop.Ide.TypeSystem project.Modified -= OnProjectModified;
}
- async Task<ProjectInfo> LoadProject (MonoDevelop.Projects.Project p, CancellationToken token)
+ internal async Task<ProjectInfo> LoadProject (MonoDevelop.Projects.Project p, CancellationToken token, MonoDevelop.Projects.Project oldProject)
{
if (!projectIdMap.ContainsKey (p)) {
p.Modified += OnProjectModified;
}
+ if (oldProject != null) {
+ lock (projectIdMap) {
+ oldProject.Modified -= OnProjectModified;
+ projectIdMap.TryRemove (oldProject, out var id);
+ projectIdMap[p] = id;
+ projectIdToMdProjectMap = projectIdToMdProjectMap.SetItem (id, p);
+ }
+ }
+
var projectId = GetOrCreateProjectId (p);
//when reloading e.g. after a save, preserve document IDs
@@ -600,6 +614,13 @@ namespace MonoDevelop.Ide.TypeSystem }
}
}
+ var projectId = GetProjectId (p); + lock (generatedFiles) { + foreach (var generatedFile in generatedFiles) { + if (generatedFile.Key.Id.ProjectId == projectId) + documents.Add (generatedFile.Key); + }
+ }
return Tuple.Create (documents, additionalDocuments);
}
@@ -755,6 +776,27 @@ namespace MonoDevelop.Ide.TypeSystem IdeApp.Workbench.OpenDocument (doc.FilePath, mdProject, activate);
}
}
+ } +
+ readonly Dictionary<DocumentInfo, SourceTextContainer> generatedFiles = new Dictionary<DocumentInfo, SourceTextContainer> ();
+ internal void AddAndOpenDocumentInternal (DocumentInfo documentInfo, SourceTextContainer textContainer)
+ {
+ lock (generatedFiles) {
+ generatedFiles[documentInfo] = textContainer;
+ OnDocumentAdded (documentInfo);
+ OnDocumentOpened (documentInfo.Id, textContainer);
+ }
+ }
+
+ internal void CloseAndRemoveDocumentInternal (DocumentId documentId, TextLoader reloader)
+ {
+ lock (generatedFiles) {
+ var documentInfo = generatedFiles.FirstOrDefault (kvp => kvp.Key.Id == documentId).Key;
+ if (documentInfo != null && generatedFiles.Remove(documentInfo) && CurrentSolution.ContainsDocument(documentId)) {
+ OnDocumentClosed (documentId, reloader);
+ OnDocumentRemoved (documentId);
+ }
+ }
}
List<MonoDevelopSourceTextContainer> openDocuments = new List<MonoDevelopSourceTextContainer>();
@@ -822,6 +864,10 @@ namespace MonoDevelop.Ide.TypeSystem if (openDoc != null) {
openDoc.Dispose ();
openDocuments.Remove (openDoc);
+ } else {
+ //Apparently something else opened this file via AddAndOpenDocumentInternal(e.g. .cshtml)
+ //it's job of whatever opened to also call CloseAndRemoveDocumentInternal
+ return;
}
}
if (!CurrentSolution.ContainsDocument (analysisDocument))
@@ -865,6 +911,13 @@ namespace MonoDevelop.Ide.TypeSystem var document = GetDocument (id);
if (document == null)
return;
+
+ var hostDocument = MonoDevelopHostDocumentRegistration.FromDocument (document);
+ if (hostDocument != null) {
+ hostDocument.UpdateText (text);
+ return;
+ }
+
bool isOpen;
var filePath = document.FilePath;
Projection projection = null;
@@ -1204,6 +1257,11 @@ namespace MonoDevelop.Ide.TypeSystem }
var path = DetermineFilePath (info.Id, info.Name, info.FilePath, info.Folders, mdProject?.FileName.ParentDirectory, true);
+ // If file is already part of project don't re-add it, example of this is .cshtml
+ if (mdProject?.IsFileInProject (path) == true) {
+ this.OnDocumentAdded (info);
+ return;
+ }
info = info.WithFilePath (path).WithTextLoader (new MonoDevelopTextLoader (path));
string formattedText;
@@ -1297,6 +1355,22 @@ namespace MonoDevelop.Ide.TypeSystem var path = GetMetadataPath (metadataReference);
if (mdProject == null || path == null)
return;
+ foreach (var r in mdProject.References) {
+ if (r.ReferenceType == MonoDevelop.Projects.ReferenceType.Assembly && r.Reference == path) {
+ LoggingService.LogWarning ("Warning duplicate reference is added " + path);
+ return;
+ }
+
+ if (r.ReferenceType == MonoDevelop.Projects.ReferenceType.Project) {
+ foreach (var fn in r.GetReferencedFileNames (MonoDevelop.Projects.ConfigurationSelector.Default)) {
+ if (fn == path) {
+ LoggingService.LogWarning ("Warning duplicate reference is added " + path + " for project " + r.Reference);
+ return;
+ }
+ }
+ }
+ }
+
mdProject.AddReference (path);
tryApplyState_changedProjects.Add (mdProject);
this.OnMetadataReferenceAdded (projectId, metadataReference);
@@ -1435,7 +1509,13 @@ namespace MonoDevelop.Ide.TypeSystem return;
var projectId = GetProjectId (project);
if (CurrentSolution.ContainsProject (projectId)) {
- HandleActiveConfigurationChanged (this, EventArgs.Empty);
+ var projectInfo = LoadProject (project, CancellationToken.None, null).ContinueWith (t => { + if (t.IsFaulted) {
+ LoggingService.LogError ("Failed to reload project", t.Exception);
+ return;
+ } + OnProjectReloaded (t.Result);
+ });
} else {
modifiedProjects.Add (project);
}
@@ -1467,7 +1547,6 @@ namespace MonoDevelop.Ide.TypeSystem originalOffset = offset;
return false;
}
-
}
// static class MonoDevelopWorkspaceFeatures
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NR5CompatibiltyExtensions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NR5CompatibiltyExtensions.cs index de720d862a..e0d7f7f003 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NR5CompatibiltyExtensions.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NR5CompatibiltyExtensions.cs @@ -60,7 +60,7 @@ namespace MonoDevelop.Ide.TypeSystem /// <param name="symbol">Symbol.</param> public static string GetFullMetadataName (this INamedTypeSymbol symbol) { - var fullName = new StringBuilder (symbol.MetadataName); + var fullName = StringBuilderCache.Allocate (symbol.MetadataName); var parentType = symbol.ContainingType; while (parentType != null) { fullName.Insert (0, '+'); @@ -73,7 +73,7 @@ namespace MonoDevelop.Ide.TypeSystem fullName.Insert (0, ns.MetadataName); ns = ns.ContainingNamespace; } - return fullName.ToString (); + return StringBuilderCache.ReturnAndFree (fullName); } /// <summary> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs index e337d273b1..378afdf996 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs @@ -314,7 +314,7 @@ namespace MonoDevelop.Ide.TypeSystem { var derivedDataPath = UserProfile.Current.CacheDir.Combine ("DerivedData"); - var name = new StringBuilder (); + var name = StringBuilderCache.Allocate (); foreach (var ch in framework.Name) { if (char.IsLetterOrDigit (ch)) { name.Append (ch); @@ -323,7 +323,7 @@ namespace MonoDevelop.Ide.TypeSystem } } - string result = derivedDataPath.Combine (name.ToString ()); + string result = derivedDataPath.Combine (StringBuilderCache.ReturnAndFree (name)); try { if (!Directory.Exists (result)) Directory.CreateDirectory (result); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs index ec571ac1af..43468257e5 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService_WorkspaceHandling.cs @@ -41,7 +41,8 @@ namespace MonoDevelop.Ide.TypeSystem { public static partial class TypeSystemService { - static readonly MonoDevelopWorkspace emptyWorkspace; + //Internal for unit test + internal static readonly MonoDevelopWorkspace emptyWorkspace; static object workspaceLock = new object(); static ImmutableList<MonoDevelopWorkspace> workspaces = ImmutableList<MonoDevelopWorkspace>.Empty; @@ -63,8 +64,8 @@ namespace MonoDevelop.Ide.TypeSystem return ws; } return emptyWorkspace; - } - + } + public static async Task<MonoDevelopWorkspace> GetWorkspaceAsync (MonoDevelop.Projects.Solution solution, CancellationToken cancellationToken = default (CancellationToken)) { var workspace = GetWorkspace (solution); @@ -116,42 +117,47 @@ namespace MonoDevelop.Ide.TypeSystem internal static async Task<List<MonoDevelopWorkspace>> Load (WorkspaceItem item, ProgressMonitor progressMonitor, CancellationToken cancellationToken = default (CancellationToken), bool showStatusIcon = true) { using (Counters.ParserService.WorkspaceItemLoaded.BeginTiming ()) { - var wsList = new List<MonoDevelopWorkspace> (); + var wsList = CreateWorkspaces (item).ToList(); //If we want BeginTiming to work correctly we need to `await` - return await InternalLoad (wsList, item, progressMonitor, cancellationToken, showStatusIcon).ContinueWith (t => { t.Wait (); return wsList; }); + await InternalLoad (wsList, progressMonitor, cancellationToken, showStatusIcon).ConfigureAwait (false); + return wsList.ToList (); } } - static Task InternalLoad (List<MonoDevelopWorkspace> list, MonoDevelop.Projects.WorkspaceItem item, ProgressMonitor progressMonitor, CancellationToken cancellationToken = default(CancellationToken), bool showStatusIcon = true) + static IEnumerable<MonoDevelopWorkspace> CreateWorkspaces (WorkspaceItem item) { - return Task.Run (async () => { - var ws = item as MonoDevelop.Projects.Workspace; - if (ws != null) { - foreach (var it in ws.Items) { - await InternalLoad (list, it, progressMonitor, cancellationToken).ConfigureAwait (false); - } - ws.ItemAdded += OnWorkspaceItemAdded; - ws.ItemRemoved += OnWorkspaceItemRemoved; - } else { - var solution = item as MonoDevelop.Projects.Solution; - if (solution != null) { - var workspace = new MonoDevelopWorkspace (solution); - lock (workspaceLock) - workspaces = workspaces.Add (workspace); - list.Add (workspace); - if (showStatusIcon) - workspace.ShowStatusIcon (); - await workspace.TryLoadSolution (cancellationToken).ConfigureAwait (false); - solution.SolutionItemAdded += OnSolutionItemAdded; - solution.SolutionItemRemoved += OnSolutionItemRemoved; - TaskCompletionSource<MonoDevelopWorkspace> request; - if (workspaceRequests.TryGetValue (solution, out request)) - request.TrySetResult (workspace); - if (showStatusIcon) - workspace.HideStatusIcon (); + if (item is MonoDevelop.Projects.Workspace ws) { + foreach (var wsItem in ws.Items) { + foreach (var mdWorkspace in CreateWorkspaces (wsItem)) { + yield return mdWorkspace; } } - }); + ws.ItemAdded += OnWorkspaceItemAdded; + ws.ItemRemoved += OnWorkspaceItemRemoved; + } else if (item is MonoDevelop.Projects.Solution solution) { + var workspace = new MonoDevelopWorkspace (solution); + lock (workspaceLock) + workspaces = workspaces.Add (workspace); + solution.SolutionItemAdded += OnSolutionItemAdded; + solution.SolutionItemRemoved += OnSolutionItemRemoved; + yield return workspace; + } + } + + static async Task InternalLoad (List<MonoDevelopWorkspace> mdWorkspaces, ProgressMonitor progressMonitor, CancellationToken cancellationToken = default (CancellationToken), bool showStatusIcon = true) + { + foreach (var workspace in mdWorkspaces) { + if (showStatusIcon) + workspace.ShowStatusIcon (); + + await workspace.TryLoadSolution (cancellationToken).ConfigureAwait (false); + TaskCompletionSource<MonoDevelopWorkspace> request; + if (workspaceRequests.TryGetValue (workspace.MonoDevelopSolution, out request)) + request.TrySetResult (workspace); + if (showStatusIcon) + workspace.HideStatusIcon (); + + } } internal static void Unload (MonoDevelop.Projects.WorkspaceItem item) @@ -244,7 +250,7 @@ namespace MonoDevelop.Ide.TypeSystem if (project == null) throw new ArgumentNullException (nameof(project)); foreach (var w in workspaces) { - var projectId = w.GetProjectId (project); + var projectId = w.GetProjectId (project); if (projectId != null) return w.CurrentSolution.GetProject (projectId); } @@ -256,7 +262,29 @@ namespace MonoDevelop.Ide.TypeSystem var parentSolution = project.ParentSolution; var workspace = await GetWorkspaceAsync (parentSolution, cancellationToken); var projectId = workspace.GetProjectId (project); - return projectId == null ? null : workspace.CurrentSolution.GetProject (projectId); + if (projectId == null) + return null; + var proj = workspace.CurrentSolution.GetProject (projectId); + if (proj != null) + return proj; + //We assume that since we have projectId and project is not found in solution + //project is being loaded(waiting MSBuild to return list of source files) + var taskSource = new TaskCompletionSource<Microsoft.CodeAnalysis.Project> (); + EventHandler<WorkspaceChangeEventArgs> del = (s, e) => { + if (e.Kind == WorkspaceChangeKind.SolutionAdded || e.Kind == WorkspaceChangeKind.SolutionReloaded) { + proj = workspace.CurrentSolution.GetProject (projectId); + if (proj != null) + taskSource.SetResult (proj); + } + }; + cancellationToken.Register (taskSource.SetCanceled); + workspace.WorkspaceChanged += del; + try { + proj = await taskSource.Task; + } finally { + workspace.WorkspaceChanged -= del; + } + return proj; } public static Task<Compilation> GetCompilationAsync (MonoDevelop.Projects.Project project, CancellationToken cancellationToken = default(CancellationToken)) @@ -264,7 +292,7 @@ namespace MonoDevelop.Ide.TypeSystem if (project == null) throw new ArgumentNullException (nameof(project)); foreach (var w in workspaces) { - var projectId = w.GetProjectId (project); + var projectId = w.GetProjectId (project); if (projectId == null) continue; var roslynProject = w.CurrentSolution.GetProject (projectId); @@ -277,7 +305,7 @@ namespace MonoDevelop.Ide.TypeSystem static void OnWorkspaceItemAdded (object s, MonoDevelop.Projects.WorkspaceItemEventArgs args) { - Task.Run (() => TypeSystemService.Load (args.Item, null)); + TypeSystemService.Load (args.Item, null).Ignore (); } static void OnWorkspaceItemRemoved (object s, MonoDevelop.Projects.WorkspaceItemEventArgs args) @@ -287,15 +315,29 @@ namespace MonoDevelop.Ide.TypeSystem static async void OnSolutionItemAdded (object sender, MonoDevelop.Projects.SolutionItemChangeEventArgs args) { - var project = args.SolutionItem as MonoDevelop.Projects.Project; - if (project != null) { - Unload (project.ParentSolution); - await Load (project.ParentSolution, new ProgressMonitor()); + try { + var project = args.SolutionItem as MonoDevelop.Projects.Project; + if (project != null) { + var ws = GetWorkspace (args.Solution); + var projectInfo = await ws.LoadProject (project, CancellationToken.None, args.ReplacedItem as MonoDevelop.Projects.Project); + if (args.Reloading) { + ws.OnProjectReloaded (projectInfo); + } + else { + ws.OnProjectAdded (projectInfo); + } + } + } catch (Exception ex) { + LoggingService.LogError ("OnSolutionItemAdded failed", ex); } } static void OnSolutionItemRemoved (object sender, MonoDevelop.Projects.SolutionItemChangeEventArgs args) { + if (args.Reloading) { + return; + } + var project = args.SolutionItem as MonoDevelop.Projects.Project; var solution = sender as MonoDevelop.Projects.Solution; if (project != null) { @@ -358,7 +400,7 @@ namespace MonoDevelop.Ide.TypeSystem { if (project == null) throw new ArgumentNullException (nameof(project)); - return outputTrackedProjects.Any (otp => string.Equals (otp.LanguageName, project.LanguageName, StringComparison.OrdinalIgnoreCase)) || + return outputTrackedProjects.Any (otp => string.Equals (otp.LanguageName, project.LanguageName, StringComparison.OrdinalIgnoreCase)) || project.GetTypeTags().Any (tag => outputTrackedProjects.Any (otp => string.Equals (otp.ProjectType, tag, StringComparison.OrdinalIgnoreCase))); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/WorkspaceExtensions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/WorkspaceExtensions.cs new file mode 100644 index 0000000000..580af7969a --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/WorkspaceExtensions.cs @@ -0,0 +1,46 @@ +// +// WorkspaceExtensions.cs +// +// Author: +// Kirill Osenkov <https://github.com/KirillOsenkov> +// +// Copyright (c) 2018 Microsoft +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.SolutionCrawler; + +namespace MonoDevelop.Ide.TypeSystem +{ + static class WorkspaceExtensions + { + internal static void RegisterSolutionCrawler (Workspace workspace) + { + var solutionCrawlerRegistrationService = workspace.Services.GetService<ISolutionCrawlerRegistrationService> (); + solutionCrawlerRegistrationService.Register (workspace); + } + + internal static void UnregisterSolutionCrawler (Workspace workspace) + { + var solutionCrawlerRegistrationService = workspace.Services.GetService<ISolutionCrawlerRegistrationService> (); + solutionCrawlerRegistrationService.Unregister (workspace); + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj index 37e3d30145..120048f39d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj @@ -135,14 +135,14 @@ <Reference Include="Microsoft.TemplateEngine.Utils"> <HintPath>..\..\..\packages\Microsoft.TemplateEngine.Utils.1.0.0-beta3-20171117-314\lib\net45\Microsoft.TemplateEngine.Utils.dll</HintPath> </Reference> - <Reference Include="Microsoft.VisualStudio.Composition, Version=15.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> - <HintPath>..\..\..\packages\Microsoft.VisualStudio.Composition.15.3.38\lib\net45\Microsoft.VisualStudio.Composition.dll</HintPath> + <Reference Include="Microsoft.VisualStudio.Composition, Version=15.6.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <HintPath>..\..\..\packages\Microsoft.VisualStudio.Composition.15.6.36\lib\net45\Microsoft.VisualStudio.Composition.dll</HintPath> </Reference> - <Reference Include="Microsoft.VisualStudio.Threading, Version=15.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> - <HintPath>..\..\..\packages\Microsoft.VisualStudio.Threading.15.4.4\lib\net45\Microsoft.VisualStudio.Threading.dll</HintPath> + <Reference Include="Microsoft.VisualStudio.Threading, Version=15.6.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <HintPath>..\..\..\packages\Microsoft.VisualStudio.Threading.15.6.46\lib\net45\Microsoft.VisualStudio.Threading.dll</HintPath> </Reference> <Reference Include="Microsoft.VisualStudio.Validation, Version=15.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> - <HintPath>..\..\..\packages\Microsoft.VisualStudio.Validation.15.3.15\lib\net45\Microsoft.VisualStudio.Validation.dll</HintPath> + <HintPath>..\..\..\packages\Microsoft.VisualStudio.Validation.15.3.32\lib\net45\Microsoft.VisualStudio.Validation.dll</HintPath> <Private>True</Private> </Reference> <Reference Include="Mono.Cairo" /> @@ -211,6 +211,10 @@ <HintPath>..\..\..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath> <Private>False</Private> </Reference> + <Reference Include="Microsoft.VisualStudio.CodingConventions"> + <HintPath>..\..\..\build\bin\Microsoft.VisualStudio.CodingConventions.dll</HintPath> + <Private>False</Private> + </Reference> </ItemGroup> <ItemGroup> <ProjectReference Include="..\MonoDevelop.Core\MonoDevelop.Core.csproj"> @@ -8561,10 +8565,16 @@ <Compile Include="MonoDevelop.Ide.Commands\ToolsCommands.cs" /> <Compile Include="MonoDevelop.Ide.Commands\ViewCommands.cs" /> <Compile Include="MonoDevelop.Ide.Commands\WindowCommands.cs" /> + <Compile Include="MonoDevelop.Ide.Composition\CommonEditorAssetServiceFactory.cs" /> <Compile Include="MonoDevelop.Ide.Composition\CompositionManager.cs" /> + <Compile Include="MonoDevelop.Ide.Composition\InlineRenameService.cs" /> <Compile Include="MonoDevelop.Ide.Composition\JoinableTaskContextHost.cs" /> <Compile Include="MonoDevelop.Ide.Composition\PlatformCatalog.cs" /> <Compile Include="MonoDevelop.Ide.Composition\PlatformExtensions.cs" /> + <Compile Include="MonoDevelop.Ide.Composition\PreviewFactoryService.cs" /> + <Compile Include="MonoDevelop.Ide.Composition\RoslynWaitIndicator.cs" /> + <Compile Include="MonoDevelop.Ide.Composition\StreamingFindUsagesPresenter.cs" /> + <Compile Include="MonoDevelop.Ide.Composition\SuggestedActionCategoryRegistryService.cs" /> <Compile Include="MonoDevelop.Ide.Editor\ITextEditorFactoryService.cs" /> <Compile Include="MonoDevelop.Ide.Gui\DisplayBindingService.cs" /> <Compile Include="MonoDevelop.Ide.Gui\BackgroundProgressMonitor.cs" /> @@ -8643,6 +8653,7 @@ <Compile Include="MonoDevelop.Ide.Gui\Workbench.cs" /> <Compile Include="MonoDevelop.Ide.Gui\StartupInfo.cs" /> <Compile Include="MonoDevelop.Ide.Gui\ProgressMonitors.cs" /> + <Compile Include="MonoDevelop.Ide.TypeSystem\IMonoDevelopHostDocument.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\MonoDevelopPersistentStorageLocationService.cs" /> <Compile Include="MonoDevelop.Ide\RoslynLogger.cs" /> <Compile Include="MonoDevelop.Ide\Services.cs" /> @@ -9274,6 +9285,7 @@ <Compile Include="MonoDevelop.Components\ImageLoader.cs" /> <Compile Include="MonoDevelop.Ide.CodeCompletion\ParameterHintingData.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\MonoDevelopWorkspace.cs" /> + <Compile Include="MonoDevelop.Ide.TypeSystem\WorkspaceExtensions.cs" /> <Compile Include="MonoDevelop.Ide.Editor\IDocumentLine.cs" /> <Compile Include="MonoDevelop.Ide.CodeTemplates\IListDataProvider.cs" /> <Compile Include="MonoDevelop.Ide.Editor\ITextEditorOptions.cs" /> @@ -9343,7 +9355,7 @@ <Compile Include="MonoDevelop.Ide.Editor\TextMarkerFactory.cs" /> <Compile Include="MonoDevelop.Ide.Editor\InternalExtensionAPI\ITextEditorImpl.cs" /> <Compile Include="MonoDevelop.Ide.Editor\InternalExtensionAPI\ITextMarkerFactory.cs" /> - <Compile Include="MonoDevelop.Ide.Editor\InternalExtensionAPI\IEditorActionHost.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\InternalExtensionAPI\IMonoDevelopEditorOperations.cs" /> <Compile Include="MonoDevelop.Ide.Editor\EditActions.cs" /> <Compile Include="MonoDevelop.Ide.Editor\DocumentContext.cs" /> <Compile Include="MonoDevelop.Ide.Editor\InternalExtensionAPI\ITextEditorFactory.cs" /> @@ -9615,7 +9627,6 @@ <Compile Include="MonoDevelop.Ide.Projects\LanguageCellRenderer.cs" /> <Compile Include="MonoDevelop.Components\RestartPanel.cs" /> <Compile Include="MonoDevelop.Ide.TypeSystem\MonoDevelopSourceText.cs" /> - <Compile Include="MonoDevelop.Ide.CodeCompletion\CompletionPresenterSession.cs" /> <Compile Include="MonoDevelop.Ide.CodeCompletion\TaggedTextUtil.cs" /> <Compile Include="MonoDevelop.Ide.CodeCompletion\SignatureHelpParameterHintingData.cs" /> <Compile Include="MonoDevelop.Ide.CodeCompletion\RoslynCompletionData.cs" /> @@ -9631,8 +9642,17 @@ <Compile Include="MonoDevelop.Ide.CodeCompletion\KeyActions.cs" /> <Compile Include="MonoDevelop.Ide.CodeCompletion\IListDataProvider.cs" /> <Compile Include="MonoDevelop.Ide.Gui.OptionPanels\D152AccessibilityPanel.cs" /> + <Compile Include="MonoDevelop.Ide.Editor\EditorConfigService.cs" /> <Compile Include="MonoDevelop.Ide.Gui\StartupAssetType.cs" /> <Compile Include="MonoDevelop.Ide.Gui\DocumentRegistry.cs" /> + <Compile Include="MonoDevelop.Ide.Codons\ItemTemplateExtensionNode.cs" /> + <Compile Include="MonoDevelop.Ide.Templates\MicrosoftTemplateEngineItemTemplatingProvider.cs" /> + <Compile Include="MonoDevelop.Ide.Templates\ItemTemplate.cs" /> + <Compile Include="MonoDevelop.Ide.Templates\MicrosoftTemplateEngineItemTemplate.cs" /> + <Compile Include="MonoDevelop.Ide.Templates\MicrosoftTemplateEngine.cs" /> + <Compile Include="MonoDevelop.Ide.Templates\NewItemConfiguration.cs" /> + <Compile Include="MonoDevelop.Ide.Templates\ItemTemplatePackageInstaller.cs" /> + <Compile Include="MonoDevelop.Ide.Templates\TemplatePackageReference.cs" /> </ItemGroup> <ItemGroup> <None Include="Makefile.am" /> @@ -9678,6 +9698,7 @@ <Folder Include="MonoDevelop.Components\Xwt\" /> <Folder Include="MonoDevelop.Components.AtkCocoaHelper\" /> <Folder Include="MonoDevelop.Ide.Editor.Highlighting\syntaxes\Markdown\" /> + <Folder Include="MonoDevelop.Ide.EditorConfig\" /> </ItemGroup> <ItemGroup> <Content Include="gtkrc"> @@ -9728,9 +9749,14 @@ <InternalsVisibleTo Include="MonoDevelop.Refactoring.Tests" /> <InternalsVisibleTo Include="Xamarin.Forms.Addin" /> <InternalsVisibleTo Include="MonoDevelop.TextEditor.Tests" /> + <InternalsVisibleTo Include="WebToolingAddin" /> + <InternalsVisibleTo Include="Xamarin.Designer.MonoDevelop" /> + <InternalsVisibleTo Include="Xamarin.AndroidDesigner.MonoDevelop" /> + <InternalsVisibleTo Include="Xamarin.iOSDesigner.MonoDevelop" /> + <InternalsVisibleTo Include="Xamarin.FormsPreviewer.MonoDevelop" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Target Name="AfterBuild"> <Copy SourceFiles="@(Data)" DestinationFolder="..\..\..\build\data\%(Data.RelativeDir)" SkipUnchangedFiles="true" /> </Target> -</Project>
\ No newline at end of file +</Project> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs index 0c6a410f2d..6d3da0c058 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DesktopService.cs @@ -400,5 +400,7 @@ namespace MonoDevelop.Ide return PlatformService.AccessibilityInUse; } } + + internal static string GetNativeRuntimeDescription () => PlatformService.GetNativeRuntimeDescription (); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DispatchService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DispatchService.cs index 7db2f6b7fd..564c6ad3bc 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DispatchService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/DispatchService.cs @@ -52,7 +52,7 @@ namespace MonoDevelop.Ide internal class TimeoutProxy { - SendOrPostCallback d; + internal SendOrPostCallback d; object state; ManualResetEventSlim resetEvent; @@ -89,8 +89,34 @@ namespace MonoDevelop.Ide [DllImport ("libglib-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] static extern uint g_timeout_add_full (int priority, uint interval, GSourceFuncInternal d, IntPtr data, GLib.DestroyNotify notify); + class ExceptionWithStackTraceWithoutThrowing : Exception + { + readonly string stacktrace; + + public ExceptionWithStackTraceWithoutThrowing (string message) : base (message) + { + stacktrace = Environment.StackTrace; + } + + public override string StackTrace => stacktrace; + } + static void AddTimeout (TimeoutProxy proxy) { + if (proxy.d == null) { + // Create an exception without throwing it, as throwing is expensive and these exceptions can be + // hit a lot of times. + + const string exceptionMessage = "Unexpected null delegate sent to synchronization context"; + LoggingService.LogInternalError (exceptionMessage, + new ExceptionWithStackTraceWithoutThrowing (exceptionMessage)); + + // Return here without queueing the UI operation. Async calls which await on the given callback + // will continue immediately, but at least we won't crash. + // Having a null continuation won't do anything anyway. + return; + } + var gch = GCHandle.Alloc (proxy); g_timeout_add_full (defaultPriority, 0, TimeoutProxy.SourceHandler, (IntPtr)gch, GLib.DestroyHelper.NotifyHandler); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs index ab8adb3045..505890829c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs @@ -534,13 +534,22 @@ namespace MonoDevelop.Ide DispatchIdleActions (500); } - + internal static bool OnExit () { - if (Exiting != null) { - ExitEventArgs args = new ExitEventArgs (); - Exiting (null, args); - return !args.Cancel; + var exiting = Exiting; + if (exiting != null) { + bool haveAnyCancelled = false; + var args = new ExitEventArgs (); + foreach (ExitEventHandler handler in exiting.GetInvocationList ()) { + try { + handler (null, args); + haveAnyCancelled |= args.Cancel; + } catch (Exception ex) { + LoggingService.LogError ("Exception processing IdeApp.Exiting handler.", ex); + } + } + return !haveAnyCancelled; } return true; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs index 87c2f014e9..b3e22ab854 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs @@ -96,6 +96,9 @@ namespace MonoDevelop.Ide return 1; SetupExceptionManager (); + // explicit GLib type system initialization for GLib < 2.36 before any other type system access + GLib.GType.Init (); + IdeApp.Customizer = options.IdeCustomizer ?? new IdeCustomizer (); try { IdeApp.Customizer.Initialize (); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeVersionInfo.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeVersionInfo.cs index 27c5b8e337..7125638f14 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeVersionInfo.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeVersionInfo.cs @@ -131,6 +131,13 @@ namespace MonoDevelop.Ide sb.Append (" (").Append (gtkTheme).AppendLine (" theme)"); else sb.AppendLine (); + + var nativeRuntime = DesktopService.GetNativeRuntimeDescription (); + if (!string.IsNullOrEmpty (nativeRuntime)) { + sb.Append ('\t'); + sb.AppendLine (nativeRuntime); + } + if (Platform.IsWindows && !IsMono ()) { using (var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SOFTWARE\Xamarin\GtkSharp\Version")) { Version ver; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ImageService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ImageService.cs index 2b7a3da97f..82c899249e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ImageService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ImageService.cs @@ -822,10 +822,10 @@ namespace MonoDevelop.Ide { var md5 = System.Security.Cryptography.MD5.Create (); byte[] hash = md5.ComputeHash (Encoding.UTF8.GetBytes (email.Trim ().ToLower ())); - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); foreach (byte b in hash) sb.Append (b.ToString ("x2")); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } public static void LoadUserIcon (this Gtk.Image image, string email, int size) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs index 0dedbf9f98..1dba998a37 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs @@ -91,7 +91,8 @@ namespace MonoDevelop.Ide public static AlertButton OverwriteFile = new AlertButton (GettextCatalog.GetString ("_Overwrite file")); public static AlertButton AddExistingFile = new AlertButton (GettextCatalog.GetString ("Add existing file")); - + public static AlertButton MakeWriteable = new AlertButton (GettextCatalog.GetString ("Make Writeable")); + public string Label { get; set; } public string Icon { get; set; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RootWorkspace.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RootWorkspace.cs index 3a88bf0b3e..c7d84187ea 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RootWorkspace.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RootWorkspace.cs @@ -426,15 +426,21 @@ namespace MonoDevelop.Ide item.Dispose (); } } - + public bool RequestItemUnload (WorkspaceObject item) { - if (ItemUnloading != null) { + var itemUnloading = ItemUnloading; + if (itemUnloading != null) { try { + bool haveAnyCancelled = false; ItemUnloadingEventArgs args = new ItemUnloadingEventArgs (item); - ItemUnloading (this, args); - return !args.Cancel; - } catch (Exception ex) { + foreach (EventHandler<ItemUnloadingEventArgs> handler in itemUnloading.GetInvocationList ()) { + handler (this, args); + haveAnyCancelled |= args.Cancel; + } + return !haveAnyCancelled; + } + catch (Exception ex) { LoggingService.LogError ("Exception in ItemUnloading.", ex); } } @@ -619,19 +625,24 @@ namespace MonoDevelop.Ide } if (IdeApp.ProjectOperations.CurrentSelectedWorkspaceItem == null) IdeApp.ProjectOperations.CurrentSelectedWorkspaceItem = GetAllSolutions ().FirstOrDefault (); - if (Items.Count == 1 && loadPreferences) { - timer.Trace ("Restoring workspace preferences"); - await RestoreWorkspacePreferences (item); - } + Document.IsInProjectSettingLoadingProcess = true; + try { + if (Items.Count == 1 && loadPreferences) { + timer.Trace ("Restoring workspace preferences"); + await RestoreWorkspacePreferences (item); + } - if (Items.Count == 1 && !reloading) - FirstWorkspaceItemRestored?.Invoke (this, new WorkspaceItemEventArgs (item)); + if (Items.Count == 1 && !reloading) + FirstWorkspaceItemRestored?.Invoke (this, new WorkspaceItemEventArgs (item)); - timer.Trace ("Reattaching documents"); - ReattachDocumentProjects (null); - monitor.ReportSuccess (GettextCatalog.GetString ("Solution loaded.")); + timer.Trace ("Reattaching documents"); + ReattachDocumentProjects (null); + monitor.ReportSuccess (GettextCatalog.GetString ("Solution loaded.")); - UpdateOpenWorkspaceItemMetadata (metadata, item); + UpdateOpenWorkspaceItemMetadata (metadata, item); + } finally { + Document.IsInProjectSettingLoadingProcess = false; + } } return true; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RoslynLogger.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RoslynLogger.cs index f33ca6475b..ed5f08c122 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RoslynLogger.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/RoslynLogger.cs @@ -5,12 +5,20 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.ErrorReporting; using MonoDevelop.Core; namespace MonoDevelop.Ide { class RoslynLogger : ILogger { + static RoslynLogger () + { + // Maybe we should crash here? + FatalError.Handler = exception => LoggingService.LogInternalError ("Roslyn fatal exception", exception); + FatalError.NonFatalHandler = exception => LoggingService.LogInternalError ("Roslyn non-fatal exception", exception); + } + public bool IsEnabled (FunctionId functionId) { // ? Maybe log more than these exceptions? http://source.roslyn.io/#Microsoft.CodeAnalysis.Workspaces/Log/FunctionId.cs,8 diff --git a/main/src/core/MonoDevelop.Ide/packages.config b/main/src/core/MonoDevelop.Ide/packages.config index 1c45ae2656..0d86112706 100644 --- a/main/src/core/MonoDevelop.Ide/packages.config +++ b/main/src/core/MonoDevelop.Ide/packages.config @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +ο»Ώ<?xml version="1.0" encoding="utf-8"?> <packages> <package id="Microsoft.TemplateEngine.Abstractions" version="1.0.0-beta3-20171117-314" targetFramework="net461" /> <package id="Microsoft.TemplateEngine.Core" version="1.0.0-beta3-20171117-314" targetFramework="net461" /> diff --git a/main/src/core/MonoDevelop.Startup/app.config b/main/src/core/MonoDevelop.Startup/app.config index 1a0b277854..11cef824bd 100644 --- a/main/src/core/MonoDevelop.Startup/app.config +++ b/main/src/core/MonoDevelop.Startup/app.config @@ -108,43 +108,43 @@ </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.CodeAnalysis" publicKeyToken="31bf3856ad364e35" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-2.7.0.0" newVersion="2.7.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-2.8.0.0" newVersion="2.8.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.CodeAnalysis.CSharp" publicKeyToken="31bf3856ad364e35" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-2.7.0.0" newVersion="2.7.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-2.8.0.0" newVersion="2.8.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.CodeAnalysis.CSharp.Workspaces" publicKeyToken="31bf3856ad364e35" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-2.7.0.0" newVersion="2.7.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-2.8.0.0" newVersion="2.8.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.CodeAnalysis.Features" publicKeyToken="31bf3856ad364e35" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-2.7.0.0" newVersion="2.7.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-2.8.0.0" newVersion="2.8.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.CodeAnalysis.EditorFeatures.Text" publicKeyToken="31bf3856ad364e35" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-2.7.0.0" newVersion="2.7.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-2.8.0.0" newVersion="2.8.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.CodeAnalysis.Workspaces" publicKeyToken="31bf3856ad364e35" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-2.7.0.0" newVersion="2.7.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-2.8.0.0" newVersion="2.8.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.CodeAnalysis.Workspaces.Desktop" publicKeyToken="31bf3856ad364e35" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-2.7.0.0" newVersion="2.7.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-2.8.0.0" newVersion="2.8.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.CodeAnalysis.VisualBasic" publicKeyToken="31bf3856ad364e35" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-2.7.0.0" newVersion="2.7.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-2.8.0.0" newVersion="2.8.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.CodeAnalysis.VisualBasic.Features" publicKeyToken="31bf3856ad364e35" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-2.7.0.0" newVersion="2.7.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-2.8.0.0" newVersion="2.8.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.CodeAnalysis.VisualBasic.Workspaces" publicKeyToken="31bf3856ad364e35" culture="neutral" /> - <bindingRedirect oldVersion="0.0.0.0-2.7.0.0" newVersion="2.7.0.0" /> + <bindingRedirect oldVersion="0.0.0.0-2.8.0.0" newVersion="2.8.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.VisualStudio.CoreUtility" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> @@ -163,6 +163,10 @@ <bindingRedirect oldVersion="0.0.0.0-15.0.0.0" newVersion="15.0.0.0" /> </dependentAssembly> <dependentAssembly> + <assemblyIdentity name="Microsoft.VisualStudio.Threading" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-15.6.0.0" newVersion="15.6.0.0" /> + </dependentAssembly> + <dependentAssembly> <assemblyIdentity name="Microsoft.VisualStudio.Validation" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-15.3.0.0" newVersion="15.3.0.0" /> </dependentAssembly> diff --git a/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests.DefaultEditActions/CaretMoveActionTests.cs b/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests.DefaultEditActions/CaretMoveActionTests.cs index a060504f0c..b5d3108a1f 100644 --- a/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests.DefaultEditActions/CaretMoveActionTests.cs +++ b/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests.DefaultEditActions/CaretMoveActionTests.cs @@ -461,6 +461,44 @@ IEnumerable<string> GetFileExtensions (string filename) "); } + [Test] + public void TestUTF32Chars_Right () + { + var data = Create (@"$π"); + CaretMoveActions.Right (data); + Check (data, @"π$"); + } + + [Test] + public void TestUTF32Chars_Left () + { + var data = Create (@"π$"); + CaretMoveActions.Left (data); + Check (data, @"$π"); + } + [Test] + public void TestUTF32Chars_Up () + { + var data = Create (@"12 +π +1$2"); + CaretMoveActions.Up (data); + Check (data, @"12 +π$ +12"); + } + + [Test] + public void TestUTF32Chars_Down () + { + var data = Create (@"1$2 +π +12"); + CaretMoveActions.Down (data); + Check (data, @"12 +π$ +12"); + } } } diff --git a/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests.DefaultEditActions/DeleteActionTests.cs b/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests.DefaultEditActions/DeleteActionTests.cs index 3989286a5d..e97d1c14a6 100644 --- a/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests.DefaultEditActions/DeleteActionTests.cs +++ b/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests.DefaultEditActions/DeleteActionTests.cs @@ -242,5 +242,21 @@ namespace Mono.TextEditor.Tests.Actions MonoDevelop.SourceEditor.EditActions.AdvancedBackspace (data); Check (data, @""" $)"); } + + [Test] + public void TestBackspaceUTF32 () + { + var data = Create (@"12π$34"); + DeleteActions.Backspace (data); + Check (data, @"12$34"); + } + + [Test] + public void TestDeleteUTF32 () + { + var data = Create (@"12$π34"); + DeleteActions.Delete (data); + Check (data, @"12$34"); + } } } diff --git a/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests.DefaultEditActions/MiscActionsTest.cs b/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests.DefaultEditActions/MiscActionsTest.cs index 67f9e3c83d..0069fe500b 100644 --- a/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests.DefaultEditActions/MiscActionsTest.cs +++ b/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests.DefaultEditActions/MiscActionsTest.cs @@ -281,6 +281,17 @@ dddddd$dddd eeeeeeeeee ffffffffff"); } + + /// <summary> + /// Bug 586125: Alt+Up at beginning of the document results in an exception + /// </summary> + [Test ()] + public void TestBug586125 () + { + TextEditorData data = Create (@"$1234567890 +1234567890"); + MiscActions.MoveBlockUp (data); + } } } diff --git a/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests/DiffTrackerTests.cs b/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests/DiffTrackerTests.cs new file mode 100644 index 0000000000..4cc2648f66 --- /dev/null +++ b/main/src/core/MonoDevelop.TextEditor.Tests/Mono.TextEditor.Tests/DiffTrackerTests.cs @@ -0,0 +1,112 @@ +// +// DiffTrackerTests.cs +// +// Author: +// Mike KrΓΌger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using NUnit.Framework; +using System.Linq; +using Mono.TextEditor.Utils; +using System.Text; + +namespace Mono.TextEditor.Tests +{ + [TestFixture] + public class DiffTrackerTests + { + static TextDocument GetDocument () + { + TextDocument doc = new TextDocument (); + StringBuilder sb = new StringBuilder (); + for (int i = 0; i < 10; i++) + sb.AppendLine ("1234567890"); + doc.Text = sb.ToString (); + doc.DiffTracker.SetBaseDocument (doc.CreateDocumentSnapshot ()); + + return doc; + } + + [Test] + public void TestInsertChanged () + { + var doc = GetDocument (); + Assert.AreEqual (TextDocument.LineState.Unchanged, doc.DiffTracker.GetLineState (doc.GetLine (5))); + doc.InsertText (doc.GetLine (5).Offset, "Hello"); + Assert.AreEqual (TextDocument.LineState.Dirty, doc.DiffTracker.GetLineState (doc.GetLine (5))); + } + + [Test] + public void TestRemoveChanged () + { + var doc = GetDocument (); + Assert.AreEqual (TextDocument.LineState.Unchanged, doc.DiffTracker.GetLineState (doc.GetLine (5))); + doc.RemoveText (doc.GetLine (5).Offset, 1); + Assert.AreEqual (TextDocument.LineState.Dirty, doc.DiffTracker.GetLineState (doc.GetLine (5))); + } + + [Test] + public void TestInsertLine () + { + var doc = GetDocument (); + Assert.AreEqual (TextDocument.LineState.Unchanged, doc.DiffTracker.GetLineState (doc.GetLine (5))); + doc.InsertText (doc.GetLine (5).Offset, "Hello\n"); + Assert.AreEqual (TextDocument.LineState.Dirty, doc.DiffTracker.GetLineState (doc.GetLine (5))); + } + + [Test] + public void TestRemoveLine () + { + var doc = GetDocument (); + Assert.AreEqual (TextDocument.LineState.Unchanged, doc.DiffTracker.GetLineState (doc.GetLine (5))); + doc.RemoveText (doc.GetLine (5).Offset, doc.GetLine (5).LengthIncludingDelimiter); + Assert.AreEqual (TextDocument.LineState.Dirty, doc.DiffTracker.GetLineState (doc.GetLine (5))); + } + + [Test] + public void TestLowerLineChangeOnInsert () + { + var doc = GetDocument (); + Assert.AreEqual (TextDocument.LineState.Unchanged, doc.DiffTracker.GetLineState (doc.GetLine (5))); + doc.InsertText (doc.GetLine (7).Offset, "Hello\n"); + doc.InsertText (doc.GetLine (5).Offset, "Hello\n"); + Assert.AreEqual (TextDocument.LineState.Dirty, doc.DiffTracker.GetLineState (doc.GetLine (5))); + Assert.AreEqual (TextDocument.LineState.Unchanged, doc.DiffTracker.GetLineState (doc.GetLine (7))); + Assert.AreEqual (TextDocument.LineState.Dirty, doc.DiffTracker.GetLineState (doc.GetLine (8))); + } + + + [Test] + public void TestLowerLineChangeOnRemove () + { + var doc = GetDocument (); + Assert.AreEqual (TextDocument.LineState.Unchanged, doc.DiffTracker.GetLineState (doc.GetLine (5))); + doc.InsertText (doc.GetLine (7).Offset, "Hello\n"); + doc.RemoveText (doc.GetLine (5).Offset, doc.GetLine (5).LengthIncludingDelimiter); + Assert.AreEqual (TextDocument.LineState.Dirty, doc.DiffTracker.GetLineState (doc.GetLine (5))); + Assert.AreEqual (TextDocument.LineState.Unchanged, doc.DiffTracker.GetLineState (doc.GetLine (7))); + Assert.AreEqual (TextDocument.LineState.Dirty, doc.DiffTracker.GetLineState (doc.GetLine (6))); + } + + } +} diff --git a/main/src/core/MonoDevelop.TextEditor.Tests/MonoDevelop.TextEditor.Tests.csproj b/main/src/core/MonoDevelop.TextEditor.Tests/MonoDevelop.TextEditor.Tests.csproj index ed0861d6bf..2e81e3c00d 100644 --- a/main/src/core/MonoDevelop.TextEditor.Tests/MonoDevelop.TextEditor.Tests.csproj +++ b/main/src/core/MonoDevelop.TextEditor.Tests/MonoDevelop.TextEditor.Tests.csproj @@ -80,6 +80,7 @@ <Compile Include="Mono.TextEditor.Tests\SemanticRuleTests.cs" /> <Compile Include="Mono.TextEditor.Tests\TextEditorDataTests.cs" /> <Compile Include="MonoDevelop.TextEditor.Extension\NavigationExtensionTests.cs" /> + <Compile Include="Mono.TextEditor.Tests\DiffTrackerTests.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> |