diff options
Diffstat (limited to 'main/src/core/MonoDevelop.Core')
23 files changed, 2455 insertions, 381 deletions
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/MonoTargetRuntime.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/MonoTargetRuntime.cs index 4c3e290a83..837dac0d7a 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/MonoTargetRuntime.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/MonoTargetRuntime.cs @@ -34,6 +34,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; +using MonoDevelop.Core; using MonoDevelop.Core.Execution; using MonoDevelop.Core.AddIns; using MonoDevelop.Core.Serialization; @@ -240,7 +241,7 @@ namespace MonoDevelop.Core.Assemblies return; foreach (string pcfile in GetAllPkgConfigFiles ()) { try { - ParsePCFile (FileService.ResolveFullPath (pcfile)); + ParsePCFile (new FilePath (pcfile).ResolveLinks ()); if (ShuttingDown) return; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.FileSystem/UnixFileSystemExtension.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.FileSystem/UnixFileSystemExtension.cs deleted file mode 100644 index d29c7e9772..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.FileSystem/UnixFileSystemExtension.cs +++ /dev/null @@ -1,54 +0,0 @@ -// UnixFileSystemExtension.cs -// -// Author: -// Alan McGovern <alan@xamarin.com> -// -// Copyright (c) 2011 Xamarin, Inc (http://www.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.Runtime.InteropServices; - -namespace MonoDevelop.Core.FileSystem -{ - class UnixFileSystemExtension : DefaultFileSystemExtension - { - const int PATHMAX = 4096 + 1; - - [DllImport ("libc")] - static extern IntPtr realpath (string path, IntPtr buffer); - - public override FilePath ResolveFullPath (FilePath path) - { - IntPtr buffer = IntPtr.Zero; - try { - buffer = Marshal.AllocHGlobal (PATHMAX); - var result = realpath (path, buffer); - return result == IntPtr.Zero ? "" : Marshal.PtrToStringAuto (buffer); - } finally { - if (buffer != IntPtr.Zero) - Marshal.FreeHGlobal (buffer); - } - } - } -} - diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/BacktrackingStringMatcher.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/BacktrackingStringMatcher.cs index 53c9f25962..d28da461a2 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/BacktrackingStringMatcher.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/BacktrackingStringMatcher.cs @@ -138,17 +138,23 @@ namespace MonoDevelop.Core.Text // letter case ch = text [j]; bool textCharIsUpper = char.IsUpper (ch); - if (!onlyWordStart && filterChar == (textCharIsUpper ? ch : char.ToUpper (ch)) && char.IsLetter (ch)) { - // cases don't match. Filter is upper char & letter is low, now prefer the match that does the word skip. - if (!(textCharIsUpper || (filterTextLowerCaseTable & flag) != 0) && j + 1 < text.Length) { - int possibleBetterResult = GetMatchChar (text, i, j + 1, onlyWordStart); - if (possibleBetterResult >= 0) - return possibleBetterResult; + if (!onlyWordStart) { + if (filterChar == (textCharIsUpper ? ch : char.ToUpper (ch)) && char.IsLetter (ch)) { + // cases don't match. Filter is upper char & letter is low, now prefer the match that does the word skip. + if (!(textCharIsUpper || (filterTextLowerCaseTable & flag) != 0) && j + 1 < text.Length) { + int possibleBetterResult = GetMatchChar (text, i, j + 1, onlyWordStart); + if (possibleBetterResult >= 0) + return possibleBetterResult; + } + return j; + } + } else { + if (textCharIsUpper && filterChar == ch && char.IsLetter (ch)) { + return j; } - return j; } + // no match, try to continue match at the next word start - bool lastWasLower = false; bool lastWasUpper = false; int wordStart = j + 1; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/ISegment.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/ISegment.cs new file mode 100644 index 0000000000..02c0acfda2 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/ISegment.cs @@ -0,0 +1,406 @@ +// +// ISegment.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2014 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; + +namespace MonoDevelop.Core.Text +{ + /// <summary> + /// An (Offset,Length)-pair. + /// </summary> + public interface ISegment + { + /// <summary> + /// Gets the start offset of the segment. + /// </summary> + int Offset { get; } + + /// <summary> + /// Gets the length of the segment. + /// </summary> + /// <remarks>For line segments (IDocumentLine), the length does not include the line delimeter.</remarks> + int Length { get; } + + /// <summary> + /// Gets the end offset of the segment. + /// </summary> + /// <remarks>EndOffset = Offset + Length;</remarks> + int EndOffset { get; } + } + + /// <summary> + /// An (Offset, Length) pair representing a text span. + /// </summary> + public struct TextSegment : IEquatable<TextSegment>, ISegment + { + public static readonly TextSegment Invalid = new TextSegment (-1, 0); + + readonly int offset; + + /// <summary> + /// Gets the start offset of the segment. + /// </summary> + /// <value> + /// The offset. + /// </value> + public int Offset { + get { + return offset; + } + } + + readonly int length; + + /// <summary> + /// Gets the length of the segment. + /// </summary> + /// <value> + /// The length. + /// </value> + public int Length { + get { + return length; + } + } + + /// <summary> + /// Gets the end offset of the segment. + /// </summary> + /// <remarks> + /// EndOffset = Offset + Length; + /// </remarks> + /// <value> + /// The end offset. + /// </value> + public int EndOffset { + get { + return Offset + Length; + } + } + + /// <summary> + /// Gets a value indicating whether this instance is empty. + /// </summary> + /// <value> + /// <c>true</c> if this instance is empty; otherwise, <c>false</c>. + /// </value> + public bool IsEmpty { + get { + return Length == 0; + } + } + + /// <summary> + /// Gets a value indicating whether this instance is invalid. + /// </summary> + /// <value> + /// <c>true</c> if this instance is invalid; otherwise, <c>false</c>. + /// </value> + public bool IsInvalid { + get { + return Offset < 0; + } + } + + /// <summary> + /// Initializes a new instance of the <see cref="TextSegment"/> struct. + /// </summary> + /// <param name='offset'> + /// The offset of the segment. + /// </param> + /// <param name='length'> + /// The length of the segment. + /// </param> + public TextSegment (int offset, int length) + { + this.offset = offset; + this.length = length; + } + + public static bool operator == (TextSegment left, TextSegment right) + { + return Equals (left, right); + } + + public static bool operator != (TextSegment left, TextSegment right) + { + return !Equals (left, right); + } + + public static bool Equals (TextSegment left, TextSegment right) + { + return left.Offset == right.Offset && left.Length == right.Length; + } + + /// <summary> + /// Determines whether this instance is inside the specified offset. + /// </summary> + /// <returns> + /// <c>true</c> if this instance is inside the specified offset (upper bound inclusive); otherwise, <c>false</c>. + /// </returns> + /// <param name='offset'> + /// The offset offset. + /// </param> + public bool IsInside (int offset) + { + return Offset <= offset && offset <= EndOffset; + } + + /// <summary> + /// Determines whether the specified <see cref="TextSegment"/> is equal to the current <see cref="TextSegment"/>. + /// </summary> + /// <param name='other'> + /// The <see cref="TextSegment"/> to compare with the current <see cref="TextSegment"/>. + /// </param> + /// <returns> + /// <c>true</c> if the specified <see cref="TextSegment"/> is equal to the current + /// <see cref="TextSegment"/>; otherwise, <c>false</c>. + /// </returns> + public bool Equals (TextSegment other) + { + return Equals (this, other); + } + + /// <summary> + /// Determines whether the specified <see cref="System.Object"/> is equal to the current <see cref="TextSegment"/>. + /// </summary> + /// <param name='obj'> + /// The <see cref="System.Object"/> to compare with the current <see cref="TextSegment"/>. + /// </param> + /// <returns> + /// <c>true</c> if the specified <see cref="System.Object"/> is equal to the current + /// <see cref="TextSegment"/>; otherwise, <c>false</c>. + /// </returns> + public override bool Equals (object obj) + { + return obj is ISegment && Equals (this, (ISegment)obj); + } + + /// <summary> + /// Serves as a hash function for a <see cref="TextSegment"/> object. + /// </summary> + /// <returns> + /// A hash code for this instance that is suitable for use in hashing algorithms and data structures such as a hash table. + /// </returns> + public override int GetHashCode () + { + return Offset ^ Length; + } + + public static TextSegment FromBounds (int startOffset, int endOffset) + { + if (startOffset > endOffset) + throw new ArgumentOutOfRangeException ("endOffset", "endOffset < startOffset"); + return new TextSegment (startOffset, endOffset - startOffset); + } + + /// <summary> + /// Returns a <see cref="System.String"/> that represents the current <see cref="TextSegment"/>. + /// </summary> + /// <returns> + /// A <see cref="System.String"/> that represents the current <see cref="TextSegment"/>. + /// </returns> + public override string ToString () + { + return string.Format ("[TextSegment: Offset={0}, Length={1}]", Offset, Length); + } + } + + /// <summary> + /// An abstract implementation of the ISegment (Offset, Length) pair representing a text span. + /// </summary> + public abstract class AbstractSegment : ISegment + { + readonly int offset; + + /// <summary> + /// Gets the start offset of the segment. + /// </summary> + /// <value> + /// The offset. + /// </value> + public int Offset { + get { + return offset; + } + } + + readonly int length; + + /// <summary> + /// Gets the length of the segment. + /// </summary> + /// <value> + /// The length. + /// </value> + public int Length { + get { + return length; + } + } + + /// <summary> + /// Gets the end offset of the segment. + /// </summary> + /// <remarks> + /// EndOffset = Offset + Length; + /// </remarks> + /// <value> + /// The end offset. + /// </value> + public int EndOffset { + get { + return Offset + Length; + } + } + + /// <summary> + /// Gets a value indicating whether this instance is empty. + /// </summary> + /// <value> + /// <c>true</c> if this instance is empty; otherwise, <c>false</c>. + /// </value> + public bool IsEmpty { + get { + return Length == 0; + } + } + + /// <summary> + /// Gets a value indicating whether this instance is invalid. + /// </summary> + /// <value> + /// <c>true</c> if this instance is invalid; otherwise, <c>false</c>. + /// </value> + public bool IsInvalid { + get { + return Offset < 0; + } + } + + /// <summary> + /// Initializes a new instance of the <see cref="TextSegment"/> struct. + /// </summary> + /// <param name='offset'> + /// The offset of the segment. + /// </param> + /// <param name='length'> + /// The length of the segment. + /// </param> + protected AbstractSegment (int offset, int length) + { + this.offset = offset; + this.length = length; + } + + + /// <summary> + /// Initializes a new instance of the <see cref="TextSegment"/> struct. + /// </summary> + protected AbstractSegment (ISegment segment) + { + if (segment == null) + throw new ArgumentNullException ("segment"); + this.offset = segment.Offset; + this.length = segment.Length; + } + } + + + /// <summary> + /// Extension methods for <see cref="ISegment"/>. + /// </summary> + public static class ISegmentExtensions + { + /// <summary> + /// Gets whether <paramref name="segment"/> fully contains the specified segment. + /// </summary> + /// <remarks> + /// Use <c>segment.Contains(offset, 0)</c> to detect whether a segment (end inclusive) contains offset; + /// use <c>segment.Contains(offset, 1)</c> to detect whether a segment (end exclusive) contains offset. + /// </remarks> + public static bool Contains (this ISegment segment, int offset, int length) + { + if (segment == null) + throw new ArgumentNullException ("segment"); + return segment.Offset <= offset && offset + length <= segment.EndOffset; + } + + /// <summary> + /// Gets whether <paramref name="segment"/> fully contains the specified segment. + /// </summary> + public static bool Contains (this ISegment segment, ISegment span) + { + if (segment == null) + throw new ArgumentNullException ("segment"); + if (span == null) + throw new ArgumentNullException ("span"); + return segment.Offset <= span.Offset && span.EndOffset <= segment.EndOffset; + } + + /// <summary> + /// Gets whether the offset is within the <paramref name="segment"/>. + /// </summary> + public static bool Contains (this ISegment segment, int offset) + { + if (segment == null) + throw new ArgumentNullException ("segment"); + return unchecked((uint)(offset - segment.Offset) < (uint)segment.Length); + } + + /// <summary> + /// Determines whether <paramref name="other"/> overlaps this span. Two spans are considered to overlap + /// if they have positions in common and neither is empty. Empty spans do not overlap with any + /// other span. + /// </summary> + public static bool OverlapsWith (this ISegment segment, ISegment other) + { + int overlapStart = Math.Max (segment.Offset, other.Offset); + int overlapEnd = Math.Min (segment.EndOffset, other.EndOffset); + return overlapStart < overlapEnd; + } + + public static ISegment AdjustSegment (this ISegment segment, TextChangeEventArgs args) + { + if (segment == null) + throw new ArgumentNullException ("segment"); + if (args.Offset < segment.Offset) + return new TextSegment (segment.Offset + args.InsertionLength - args.RemovalLength, segment.Length); + if (args.Offset <= segment.EndOffset) + return new TextSegment (segment.Offset, segment.Length); + return segment; + } + + public static IEnumerable<ISegment> AdjustSegments (this IEnumerable<ISegment> segments, TextChangeEventArgs args) + { + if (segments == null) + throw new ArgumentNullException ("segments"); + foreach (var segment in segments) { + yield return segment.AdjustSegment (args); + } + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/ITextSource.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/ITextSource.cs new file mode 100644 index 0000000000..5dcfd13c51 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/ITextSource.cs @@ -0,0 +1,206 @@ +// +// ITextSource.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2014 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; +using System.Text; + +namespace MonoDevelop.Core.Text +{ + /// <summary> + /// A read-only view on a (potentially mutable) text source. + /// The IDocument interface derives from this interface. + /// </summary> + public interface ITextSource + { + /// <summary> + /// Gets a version identifier for this text source. + /// Returns null for unversioned text sources. + /// </summary> + ITextSourceVersion Version { get; } + + /// <summary> + /// Determines if a byte order mark was read or is going to be written. + /// </summary> + bool UseBOM { get; } + + /// <summary> + /// Encoding of the text that was read from or is going to be saved to. + /// </summary> + Encoding Encoding { get; } + + /// <summary> + /// Gets the total text length. + /// </summary> + /// <returns>The length of the text, in characters.</returns> + /// <remarks>This is the same as Text.Length, but is more efficient because + /// it doesn't require creating a String object.</remarks> + int Length { get; } + + /// <summary> + /// Gets the whole text as string. + /// </summary> + [System.Diagnostics.CodeAnalysis.SuppressMessage ("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + string Text { get; } + + /// <summary> + /// Gets a character at the specified position in the document. + /// </summary> + /// <paramref name="offset">The index of the character to get.</paramref> + /// <exception cref="ArgumentOutOfRangeException">Offset is outside the valid range (0 to TextLength-1).</exception> + /// <returns>The character at the specified position.</returns> + /// <remarks>This is the same as Text[offset], but is more efficient because + /// it doesn't require creating a String object.</remarks> + char this [int offset] { get; } + + /// <summary> + /// Gets a character at the specified position in the document. + /// </summary> + /// <paramref name="offset">The index of the character to get.</paramref> + /// <exception cref="ArgumentOutOfRangeException">Offset is outside the valid range (0 to TextLength-1).</exception> + /// <returns>The character at the specified position.</returns> + /// <remarks>This is the same as Text[offset], but is more efficient because + /// it doesn't require creating a String object.</remarks> + char GetCharAt (int offset); + + /// <summary> + /// Retrieves the text for a portion of the document. + /// </summary> + /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception> + /// <remarks>This is the same as Text.Substring, but is more efficient because + /// it doesn't require creating a String object for the whole document.</remarks> + string GetTextAt (int offset, int length); + + /// <summary> + /// Creates a new TextReader to read from this text source. + /// </summary> + TextReader CreateReader (); + + /// <summary> + /// Creates a new TextReader to read from this text source. + /// </summary> + TextReader CreateReader (int offset, int length); + + /// <summary> + /// Writes the text from this document into the TextWriter. + /// </summary> + void WriteTextTo (TextWriter writer); + + /// <summary> + /// Writes the text from this document into the TextWriter. + /// </summary> + void WriteTextTo (TextWriter writer, int offset, int length); + + /// <summary> + /// Creates an immutable snapshot of this text source. + /// Unlike all other methods in this interface, this method is thread-safe. + /// </summary> + ITextSource CreateSnapshot (); + + /// <summary> + /// Creates an immutable snapshot of a part of this text source. + /// Unlike all other methods in this interface, this method is thread-safe. + /// </summary> + ITextSource CreateSnapshot (int offset, int length); + } + + public static class TextSourceExtension + { + /// <summary> + /// Retrieves the text for a portion of the document. + /// </summary> + /// <exception cref="ArgumentOutOfRangeException">offset or length is outside the valid range.</exception> + public static string GetTextAt (this ITextSource source, ISegment segment) + { + if (source == null) + throw new ArgumentNullException ("source"); + return source.GetTextAt (segment.Offset, segment.Length); + } + + + public static string GetTextBetween (this ITextSource source, int startOffset, int endOffset) + { + if (source == null) + throw new ArgumentNullException ("source"); + if (startOffset < 0 || startOffset > source.Length) + throw new ArgumentNullException ("startOffset"); + if (endOffset < 0 || endOffset > source.Length) + throw new ArgumentNullException ("endOffset"); + if (startOffset > endOffset) + throw new InvalidOperationException (); + return source.GetTextAt (startOffset, endOffset - startOffset); + } + + + /// <summary> + /// Writes the text from this document into a file. + /// </summary> + public static void WriteTextTo (this ITextSource source, string fileName) + { + if (source == null) + throw new ArgumentNullException ("source"); + TextFileUtility.WriteText (fileName, source.Text, source.Encoding, source.UseBOM); + } + + /// <summary> + /// Writes the text from this document into the TextWriter. + /// </summary> + public static void WriteTextTo (this ITextSource source, TextWriter writer, ISegment segment) + { + if (source == null) + throw new ArgumentNullException ("source"); + if (writer == null) + throw new ArgumentNullException ("writer"); + if (segment == null) + throw new ArgumentNullException ("segment"); + source.WriteTextTo (writer, segment.Offset, segment.Length); + } + + /// <summary> + /// Creates a new TextReader to read from this text source. + /// </summary> + public static TextReader CreateReader (this ITextSource source, ISegment segment) + { + if (source == null) + throw new ArgumentNullException ("source"); + if (segment == null) + throw new ArgumentNullException ("segment"); + return source.CreateReader (segment.Offset, segment.Length); + } + + /// <summary> + /// Creates an immutable snapshot of a part of this text source. + /// Unlike all other methods in this interface, this method is thread-safe. + /// </summary> + public static ITextSource CreateSnapshot (this ITextSource source, ISegment segment) + { + if (source == null) + throw new ArgumentNullException ("source"); + if (segment == null) + throw new ArgumentNullException ("segment"); + return source.CreateSnapshot (segment.Offset, segment.Length); + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/ITextSourceVersion.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/ITextSourceVersion.cs new file mode 100644 index 0000000000..e63b8214fd --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/ITextSourceVersion.cs @@ -0,0 +1,74 @@ +// +// ITextSourceVersion.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2014 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; + +namespace MonoDevelop.Core.Text +{ + /// <summary> + /// Represents a version identifier for a text source. + /// </summary> + /// <remarks> + /// Verions can be used to efficiently detect whether a document has changed and needs reparsing; + /// or even to implement incremental parsers. + /// It is a separate class from ITextSource to allow the GC to collect the text source while + /// the version checkpoint is still in use. + /// </remarks> + public interface ITextSourceVersion + { + /// <summary> + /// Gets whether this checkpoint belongs to the same document as the other checkpoint. + /// </summary> + /// <remarks> + /// Returns false when given <c>null</c>. + /// </remarks> + bool BelongsToSameDocumentAs (ITextSourceVersion other); + + /// <summary> + /// Compares the age of this checkpoint to the other checkpoint. + /// </summary> + /// <remarks>This method is thread-safe.</remarks> + /// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this version.</exception> + /// <returns>-1 if this version is older than <paramref name="other"/>. + /// 0 if <c>this</c> version instance represents the same version as <paramref name="other"/>. + /// 1 if this version is newer than <paramref name="other"/>.</returns> + int CompareAge (ITextSourceVersion other); + + /// <summary> + /// Gets the changes from this checkpoint to the other checkpoint. + /// If 'other' is older than this checkpoint, reverse changes are calculated. + /// </summary> + /// <remarks>This method is thread-safe.</remarks> + /// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception> + IEnumerable<TextChangeEventArgs> GetChangesTo (ITextSourceVersion other); + + /// <summary> + /// Calculates where the offset has moved in the other buffer version. + /// </summary> + /// <exception cref="ArgumentException">Raised if 'other' belongs to a different document than this checkpoint.</exception> + int MoveOffsetTo (ITextSourceVersion other, int oldOffset); + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/StringTextSource.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/StringTextSource.cs new file mode 100644 index 0000000000..d223d16bc7 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/StringTextSource.cs @@ -0,0 +1,173 @@ +// +// StringTextSource.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2014 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.Text; +using System.IO; + +namespace MonoDevelop.Core.Text +{ + /// <summary> + /// Implements the ITextSource interface using a string. + /// Note that objects from this class are immutable. + /// </summary> + [Serializable] + public class StringTextSource : ITextSource + { + /// <summary> + /// Gets a text source containing the empty string. + /// </summary> + public static readonly StringTextSource Empty = new StringTextSource (string.Empty); + + readonly string text; + readonly ITextSourceVersion version; + + /// <summary> + /// Determines if a byte order mark was read or is going to be written. + /// </summary> + public bool UseBOM { get; private set; } + + /// <summary> + /// Encoding of the text that was read from or is going to be saved to. + /// </summary> + public Encoding Encoding { get; private set; } + + /// <summary> + /// Creates a new StringTextSource with the given text. + /// </summary> + public StringTextSource (string text, Encoding encoding = null, bool useBom = true) + { + if (text == null) + throw new ArgumentNullException ("text"); + this.text = text; + this.UseBOM = useBom; + this.Encoding = encoding ?? Encoding.UTF8; + } + + /// <summary> + /// Creates a new StringTextSource with the given text. + /// </summary> + public StringTextSource (string text, ITextSourceVersion version, Encoding encoding = null, bool useBom = true) + { + if (text == null) + throw new ArgumentNullException ("text"); + this.text = text; + this.version = version; + this.UseBOM = useBom; + this.Encoding = encoding ?? Encoding.UTF8; + } + + /// <inheritdoc/> + public ITextSourceVersion Version { + get { return version; } + } + + /// <inheritdoc/> + public int Length { + get { return text.Length; } + } + + /// <inheritdoc/> + public string Text { + get { return text; } + } + + /// <inheritdoc/> + public ITextSource CreateSnapshot () + { + return this; // StringTextSource is immutable + } + + /// <inheritdoc/> + public ITextSource CreateSnapshot (int offset, int length) + { + return new StringTextSource (text.Substring (offset, length)); + } + + /// <inheritdoc/> + public char GetCharAt (int offset) + { + return text [offset]; + } + + public char this [int offset] { + get { + return text [offset]; + } + } + + /// <inheritdoc/> + public string GetTextAt (int offset, int length) + { + return text.Substring (offset, length); + } + + public StringTextSource WithEncoding (Encoding encoding) + { + return new StringTextSource (text, encoding, UseBOM); + } + + public StringTextSource WithBom (bool useBom) + { + return new StringTextSource (text, Encoding, useBom); + } + + public static StringTextSource ReadFrom (string fileName) + { + bool hadBom; + Encoding encoding; + var text = TextFileUtility.ReadAllText (fileName, out hadBom, out encoding); + return new StringTextSource (text, encoding, hadBom); + } + + /// <inheritdoc/> + public TextReader CreateReader () + { + return new StringReader (text); + } + + /// <inheritdoc/> + public TextReader CreateReader (int offset, int length) + { + return new StringReader (text.Substring (offset, length)); + } + + /// <inheritdoc/> + public void WriteTextTo (TextWriter writer) + { + if (writer == null) + throw new ArgumentNullException ("writer"); + writer.Write (text); + } + + /// <inheritdoc/> + public void WriteTextTo (TextWriter writer, int offset, int length) + { + if (writer == null) + throw new ArgumentNullException ("writer"); + writer.Write (text.Substring (offset, length)); + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextChangeEventArgs.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextChangeEventArgs.cs new file mode 100644 index 0000000000..ccd4033844 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextChangeEventArgs.cs @@ -0,0 +1,135 @@ +// +// TextChangeEventArgs.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2014 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; + +namespace MonoDevelop.Core.Text +{ + /// <summary> + /// Describes a change of the document text. + /// This class is thread-safe. + /// </summary> + [Serializable] + public class TextChangeEventArgs : EventArgs + { + readonly int offset; + readonly ITextSource removedText; + readonly ITextSource insertedText; + + /// <summary> + /// The offset at which the change occurs. + /// </summary> + public int Offset { + get { return offset; } + } + + /// <summary> + /// The text that was removed. + /// </summary> + public ITextSource RemovedText { + get { return removedText; } + } + + /// <summary> + /// The number of characters removed. + /// </summary> + public int RemovalLength { + get { return removedText.Length; } + } + + /// <summary> + /// The text that was inserted. + /// </summary> + public ITextSource InsertedText { + get { return insertedText; } + } + + /// <summary> + /// The number of characters inserted. + /// </summary> + public int InsertionLength { + get { return insertedText.Length; } + } + + /// <summary> + /// InsertionLength - RemovalLength + /// </summary> + public int ChangeDelta { + get { + return InsertionLength - RemovalLength; + } + } + + /// <summary> + /// Creates a new TextChangeEventArgs object. + /// </summary> + public TextChangeEventArgs(int offset, string removedText, string insertedText) + { + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", offset, "offset must not be negative"); + this.offset = offset; + this.removedText = removedText != null ? new StringTextSource(removedText) : StringTextSource.Empty; + this.insertedText = insertedText != null ? new StringTextSource(insertedText) : StringTextSource.Empty; + } + + /// <summary> + /// Creates a new TextChangeEventArgs object. + /// </summary> + public TextChangeEventArgs(int offset, ITextSource removedText, ITextSource insertedText) + { + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", offset, "offset must not be negative"); + this.offset = offset; + this.removedText = removedText ?? StringTextSource.Empty; + this.insertedText = insertedText ?? StringTextSource.Empty; + } + + /// <summary> + /// Gets the new offset where the specified offset moves after this document change. + /// </summary> + public virtual int GetNewOffset(int offset) + { + if (offset >= this.Offset && offset <= this.Offset + this.RemovalLength) { +// if (movementType == AnchorMovementType.BeforeInsertion) +// return this.Offset; +// else + return this.Offset + this.InsertionLength; + } else if (offset > this.Offset) { + return offset + this.InsertionLength - this.RemovalLength; + } else { + return offset; + } + } + + /// <summary> + /// Creates TextChangeEventArgs for the reverse change. + /// </summary> + public virtual TextChangeEventArgs Invert() + { + return new TextChangeEventArgs(offset, insertedText, removedText); + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextFileUtility.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextFileUtility.cs new file mode 100644 index 0000000000..5d7eed6fab --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextFileUtility.cs @@ -0,0 +1,850 @@ +// +// TextFileUtility.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2014 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.Linq; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace MonoDevelop.Core.Text +{ + /// <summary> + /// This class handles text input from files, streams and byte arrays with auto-detect encoding. + /// </summary> + public static class TextFileUtility + { + readonly static int maxBomLength = 0; + readonly static Encoding[] encodingsWithBom; + + static TextFileUtility () + { + var encodings = new List<Encoding> (); + + foreach (var info in Encoding.GetEncodings ()) { + Encoding encoding; + try { + encoding = info.GetEncoding (); + } catch (NotSupportedException) { + continue; + } + var bom = encoding.GetPreamble (); + if (bom == null || bom.Length == 0) + continue; + maxBomLength = Math.Max (maxBomLength, bom.Length); + encodings.Add (encoding); + } + encodingsWithBom = encodings.ToArray (); + + // Encoding verifiers + var verifierList = new List<Verifier> { + new Utf8Verifier (), + new GB18030CodePageVerifier (), + new WindowsCodePageVerifier (), + new UnicodeVerifier (), + new BigEndianUnicodeVerifier (), + new CodePage858Verifier () + }; + + verifiers = verifierList.Where (v => v.IsSupported).ToArray (); + + // cache the verifier machine state tables, to do the virtual StateTable only once. + stateTables = new byte[verifiers.Length][][]; + for (int i = 0; i < verifiers.Length; i++) { + verifiers [i].Initialize (); + stateTables [i] = verifiers [i].StateTable; + } + } + + #region stream reader methods + public static StreamReader OpenStream (string fileName) + { + bool hadBom; + return OpenStream (File.ReadAllBytes (fileName), out hadBom); + } + + public static StreamReader OpenStream (string fileName, out bool hadBom) + { + return OpenStream (File.ReadAllBytes (fileName), out hadBom); + } + + public static StreamReader OpenStream (byte[] bytes) + { + bool hadBom; + return OpenStream (bytes, out hadBom); + } + + public static StreamReader OpenStream (byte[] bytes, out bool hadBom) + { + if (bytes == null) + throw new ArgumentNullException ("bytes"); + return OpenStream (new MemoryStream (bytes, false), out hadBom); + } + + public static StreamReader OpenStream (Stream stream) + { + bool hadBom; + return OpenStream (stream, out hadBom); + } + + public static StreamReader OpenStream (Stream stream, out bool hadBom) + { + if (stream == null) + throw new ArgumentNullException ("stream"); + byte[] possibleBom = new byte[maxBomLength]; + stream.Read (possibleBom, 0, Math.Min ((int)stream.Length, maxBomLength)); + + foreach (var encoding in encodingsWithBom) { + var bom = encoding.GetPreamble (); + bool invalid = false; + for (int i = 0; i < bom.Length; i++) { + if (bom [i] != possibleBom [i]) { + invalid = true; + break; + } + } + + if (!invalid) { + hadBom = true; + stream.Position = bom.Length; + return new StreamReader (stream, encoding); + } + } + stream.Position = 0; + hadBom = false; + return new StreamReader (stream, AutoDetectEncoding (stream)); + } + #endregion + + #region string methods + public static string GetText (byte[] bytes) + { + using (var stream = OpenStream (bytes)) { + return stream.ReadToEnd (); + } + } + + public static string GetText (byte[] bytes, out Encoding encoding, out bool hadBom) + { + if (bytes == null) + throw new ArgumentNullException ("bytes"); + using (var stream = OpenStream (bytes, out hadBom)) { + encoding = stream.CurrentEncoding; + return stream.ReadToEnd (); + } + } + + public static string GetText (byte[] bytes, Encoding encoding, out bool hadBom) + { + byte[] bom = encoding.GetPreamble (); + if (bom != null && bom.Length > 0 && bom.Length <= bytes.Length) { + hadBom = true; + for (int i = 0; i < bom.Length; i++) { + if (bytes [i] != bom [i]) { + hadBom = false; + break; + } + } + } else { + hadBom = false; + } + if (hadBom) + return encoding.GetString (bytes, bom.Length, bytes.Length - bom.Length); + return encoding.GetString (bytes); + } + + public static string GetText (Stream inputStream) + { + using (var stream = OpenStream (inputStream)) { + return stream.ReadToEnd (); + } + } + + public static string GetText (Stream inputStream, out Encoding encoding, out bool hadBom) + { + if (inputStream == null) + throw new ArgumentNullException ("inputStream"); + using (var stream = OpenStream (inputStream, out hadBom)) { + encoding = stream.CurrentEncoding; + return stream.ReadToEnd (); + } + } + + public static string GetText (string fileName) + { + using (var stream = OpenStream (fileName)) { + return stream.ReadToEnd (); + } + } + + public static string GetText (string fileName, out Encoding encoding, out bool hadBom) + { + if (fileName == null) + throw new ArgumentNullException ("fileName"); + using (var stream = OpenStream (fileName, out hadBom)) { + encoding = stream.CurrentEncoding; + return stream.ReadToEnd (); + } + } + + #endregion + + #region file methods + public static void WriteText (string fileName, string text, Encoding encoding, bool hadBom) + { + if (fileName == null) + throw new ArgumentNullException ("fileName"); + if (text == null) + throw new ArgumentNullException ("text"); + if (encoding == null) + throw new ArgumentNullException ("encoding"); + // atomic rename only works in the same directory on linux. The tmp files may be on another partition -> breaks save. + string tmpPath = Path.Combine (Path.GetDirectoryName (fileName), ".#" + Path.GetFileName (fileName)); + using (var stream = new FileStream (tmpPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write)) { + if (hadBom) { + var bom = encoding.GetPreamble (); + if (bom != null && bom.Length > 0) + stream.Write (bom, 0, bom.Length); + } + byte[] bytes = encoding.GetBytes (text); + stream.Write (bytes, 0, bytes.Length); + } + try { + SystemRename (tmpPath, fileName); + } catch (Exception) { + try { + File.Delete (tmpPath); + } catch { + // nothing + } + throw; + } + } + + /// <summary> + /// Returns a byte array containing the text encoded by a specified encoding & bom. + /// </summary> + /// <param name="text">The text to encode.</param> + /// <param name="encoding">The encoding.</param> + /// <param name="hadBom">If set to <c>true</c> a bom will be prepended.</param> + public static byte[] GetBuffer (string text, Encoding encoding, bool hadBom) + { + using (var stream = new MemoryStream ()) { + if (hadBom) { + var bom = encoding.GetPreamble (); + if (bom != null && bom.Length > 0) + stream.Write (bom, 0, bom.Length); + } + byte[] bytes = encoding.GetBytes (text); + stream.Write (bytes, 0, bytes.Length); + return stream.GetBuffer (); + } + } + + // Code taken from FileService.cs + static void SystemRename (string sourceFile, string destFile) + { + //FIXME: use the atomic System.IO.File.Replace on NTFS + if (Platform.IsWindows) { + string wtmp = null; + if (File.Exists (destFile)) { + do { + wtmp = Path.Combine (Path.GetTempPath (), Guid.NewGuid ().ToString ()); + } while (File.Exists (wtmp)); + File.Move (destFile, wtmp); + } + try { + File.Move (sourceFile, destFile); + } catch { + try { + if (wtmp != null) + File.Move (wtmp, destFile); + } catch { + wtmp = null; + } + throw; + } finally { + if (wtmp != null) { + try { + File.Delete (wtmp); + } catch { + } + } + } + } else { + Mono.Unix.Native.Syscall.rename (sourceFile, destFile); + } + } + + public static string ReadAllText (string fileName) + { + bool hadBom; + Encoding encoding; + return ReadAllText (fileName, out hadBom, out encoding); + } + + public static string ReadAllText (string fileName, out bool hadBom, out Encoding encoding) + { + if (fileName == null) + throw new ArgumentNullException ("fileName"); + using (var stream = new FileStream (fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { + return GetText (stream, out encoding, out hadBom); + } + } + + public static string ReadAllText (string fileName, Encoding encoding, out bool hadBom) + { + if (fileName == null) + throw new ArgumentNullException ("fileName"); + if (encoding == null) + throw new ArgumentNullException ("encoding"); + + byte[] content = File.ReadAllBytes (fileName); + return GetText (content, encoding, out hadBom); + } + #endregion + + #region ASCII encoding check + public static bool IsASCII (string text) + { + if (text == null) + throw new ArgumentNullException ("text"); + for (int i = 0; i < text.Length; i++) { + var ch = text [i]; + if (ch > 0x7F) + return false; + } + return true; + } + #endregion + + #region Binary check + public static bool IsBinary (byte[] bytes) + { + if (bytes == null) + throw new ArgumentNullException ("bytes"); + return IsBinary (new MemoryStream (bytes, false)); + } + + public static bool IsBinary (string fileName) + { + if (fileName == null) + throw new ArgumentNullException ("fileName"); + using (var stream = new FileStream (fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { + return IsBinary (stream); + } + } + + public static bool IsBinary (Stream stream) + { + if (stream == null) + throw new ArgumentNullException ("stream"); + + var enc = AutoDetectEncoding (stream); + return enc == Encoding.ASCII; + } + #endregion + + #region Encoding autodetection + static readonly Verifier[] verifiers; + static readonly byte[][][] stateTables; + + static unsafe Encoding AutoDetectEncoding (Stream stream) + { + try { + int max = (int)Math.Min (stream.Length, 50 * 1024); + byte[] readBuf = new byte[max]; + int readLength = stream.Read (readBuf, 0, max); + stream.Position = 0; + + // Store the dfa data from the verifiers in local variables. + byte[] states = new byte[verifiers.Length]; + int verifiersRunning = verifiers.Length; + + for (int i = 0; i < verifiers.Length; i++) + states [i] = verifiers [i].InitalState; + + // run the verifiers + fixed (byte* bBeginPtr = readBuf, stateBeginPtr = states) { + byte* bPtr = bBeginPtr; + byte* bEndPtr = bBeginPtr + readLength; + byte* sEndPtr = stateBeginPtr + states.Length; + + while (bPtr != bEndPtr) { + byte* sPtr = stateBeginPtr; + int i = 0; + while (sPtr != sEndPtr) { + byte curState = *sPtr; + if (curState != 0) { + curState = stateTables [i] [curState] [*bPtr]; + if (curState == 0) { + verifiersRunning--; + if (verifiersRunning == 0) + goto finishVerify; + } + *sPtr = curState; + } + sPtr++; + i++; + } + bPtr++; + } + finishVerify: + if (verifiersRunning > 0) { + // Console.WriteLine ("valid encodings:"); + // for (int i = 0; i < verifiers.Length; i++) { + // if (verifiers [i].IsEncodingValid (states [i])) + // Console.WriteLine (verifiers [i].Encoding.EncodingName); + // } + // Console.WriteLine ("---------------"); + for (int i = 0; i < verifiers.Length; i++) { + if (verifiers [i].IsEncodingValid (states [i])) + return verifiers [i].Encoding; + } + } + } + + } catch (Exception e) { + Console.WriteLine (e); + } + return Encoding.ASCII; + } + + abstract class Verifier + { + internal const byte Error = 0; + protected static readonly byte[] errorTable = new byte[(int)byte.MaxValue + 1]; + + public abstract byte InitalState { get; } + + public abstract Encoding Encoding { get; } + + public abstract byte[][] StateTable { get; } + + protected abstract void Init (); + + bool isInitialized = false; + + public void Initialize () + { + if (isInitialized) + throw new InvalidOperationException ("Already initialized"); + isInitialized = true; + Init (); + } + + public abstract bool IsSupported { get; } + + public virtual bool IsEncodingValid (byte state) + { + return state != Error; + } + } + + class Utf8Verifier : Verifier + { + const byte UTF1 = 1; + const byte UTFTail1 = 2; + const byte UTFTail2 = 3; + const byte UTFTail3 = 4; + const byte UTF8_3_TailPre1 = 5; + const byte UTF8_3_TailPre2 = 6; + const byte UTF8_4_TailPre1 = 7; + const byte UTF8_4_TailPre2 = 8; + const byte LAST = 9; + static byte[][] table; + + public override bool IsSupported { + get { + try { + return Encoding.UTF8 != null; + } catch (Exception) { + return false; + } + } + } + + protected override void Init () + { + table = new byte[LAST][]; + table [0] = errorTable; + for (int i = 1; i < LAST; i++) + table [i] = new byte[(int)byte.MaxValue + 1]; + + // UTF8-1 = %x00-7F + // Take out the 0 case, that indicates a UTF16/32 file. + for (int i = 0x00; i <= 0x7F; i++) { + table [UTF1] [i] = UTF1; + } + + // UTF8-tail = %x80-BF + for (int i = 0x80; i <= 0xBF; i++) { + table [UTFTail1] [i] = UTF1; + table [UTFTail2] [i] = UTFTail1; + table [UTFTail3] [i] = UTFTail2; + } + + // UTF8-2 = %xC2-DF UTF8-tail + for (int i = 0xC2; i <= 0xDF; i++) + table [UTF1] [i] = UTFTail1; + + // UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) / + // %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail ) + for (int i = 0xA0; i <= 0xBF; i++) { + table [UTF8_3_TailPre1] [i] = UTFTail1; + } + for (int i = 0x80; i <= 0x9F; i++) { + table [UTF8_3_TailPre2] [i] = UTFTail1; + } + + table [UTF1] [0xE0] = UTF8_3_TailPre1; + for (int i = 0xE1; i <= 0xEC; i++) + table [UTF1] [i] = UTFTail2; + table [UTF1] [0xED] = UTF8_3_TailPre2; + for (int i = 0xEE; i <= 0xEF; i++) + table [UTF1] [i] = UTFTail2; + + // UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) / + // %xF4 %x80-8F 2( UTF8-tail ) + + for (int i = 0x90; i <= 0xBF; i++) { + table [UTF8_4_TailPre1] [i] = UTFTail2; + } + for (int i = 0x80; i <= 0xBF; i++) { + table [UTF8_4_TailPre2] [i] = UTFTail2; + } + table [UTF1] [0xF0] = UTF8_4_TailPre1; + for (int i = 0xF1; i <= 0xF3; i++) + table [UTF1] [i] = UTFTail3; + table [UTF1] [0xF4] = UTF8_4_TailPre2; + + // always invalid. + for (int i = 0; i < table.Length; i++) { + table [i] [0xC0] = Error; + table [i] [0xC1] = Error; + table [i] [0xF5] = Error; + table [i] [0xFF] = Error; + } + } + + public override byte InitalState { get { return UTF1; } } + + public override Encoding Encoding { get { return Encoding.UTF8; } } + + public override byte[][] StateTable { get { return table; } } + } + + /// <summary> + /// Unicode verifier + /// </summary> + class UnicodeVerifier : Verifier + { + const byte Even = 1; + const byte Odd = 2; + const byte EvenPossible = 3; + const byte OddPossible = 4; + const byte LAST = 5; + static byte[][] table; + + protected override void Init () + { + // Simple approach - detect 0 at odd posititons, then it's likely a utf16 + // if 0 at an even position it's regarded as no utf-16. + table = new byte[LAST][]; + table [0] = errorTable; + for (int i = 1; i < LAST; i++) + table [i] = new byte[(int)byte.MaxValue + 1]; + + for (int i = 0x00; i <= 0xFF; i++) { + table [Even] [i] = Odd; + table [Odd] [i] = Even; + table [EvenPossible] [i] = OddPossible; + table [OddPossible] [i] = EvenPossible; + } + table [Odd] [0] = EvenPossible; + table [Even] [0] = Error; + table [EvenPossible] [0] = Error; + } + + public override byte InitalState { get { return Even; } } + + public override Encoding Encoding { get { return Encoding.Unicode; } } + + public override byte[][] StateTable { get { return table; } } + + public override bool IsSupported { + get { + try { + return Encoding.Unicode != null; + } catch (Exception) { + return false; + } + } + } + + public override bool IsEncodingValid (byte state) + { + return state == EvenPossible || state == OddPossible; + } + } + + class BigEndianUnicodeVerifier : Verifier + { + const byte Even = 1; + const byte Odd = 2; + const byte EvenPossible = 3; + const byte OddPossible = 4; + const byte LAST = 5; + + public override byte InitalState { get { return Even; } } + + public override Encoding Encoding { get { return Encoding.BigEndianUnicode; } } + + public override byte[][] StateTable { get { return table; } } + + public override bool IsSupported { + get { + try { + return Encoding.BigEndianUnicode != null; + } catch (Exception) { + return false; + } + } + } + + public override bool IsEncodingValid (byte state) + { + return state == EvenPossible || state == OddPossible; + } + + static byte[][] table; + + protected override void Init () + { + // Simple approach - detect 0 at even posititons, then it's likely a utf16be + // if 0 at an odd position it's regarded as no utf-16be. + table = new byte[LAST][]; + table [0] = errorTable; + for (int i = 1; i < LAST; i++) + table [i] = new byte[(int)byte.MaxValue + 1]; + + for (int i = 0x00; i <= 0xFF; i++) { + table [Even] [i] = Odd; + table [Odd] [i] = Even; + table [EvenPossible] [i] = OddPossible; + table [OddPossible] [i] = EvenPossible; + } + table [Odd] [0] = Error; + table [OddPossible] [0] = Error; + table [Even] [0] = OddPossible; + } + } + + /// <summary> + /// Code page 1252 was the long time default on windows. This encoding is a superset of ISO 8859-1. + /// </summary> + class WindowsCodePageVerifier : Verifier + { + const byte Valid = 1; + const byte LAST = 2; + static byte[][] table; + static Encoding EncodingWindows; + + public override byte InitalState { get { return Valid; } } + + public override Encoding Encoding { get { return EncodingWindows; } } + + public override byte[][] StateTable { get { return table; } } + + + const int westernEncodingCodePage = 1252; + /// <summary> + /// Try to guess the windows code page using the default encoding, on non windows system default + /// to 1252 (western encoding). + /// </summary> + int WindowsCodePage { + get { + if (Platform.IsWindows) { + int cp = Encoding.Default.CodePage; + if (cp >= 1250 && cp < 1260) + return cp; + } + return westernEncodingCodePage; + } + } + + public override bool IsSupported { + get { + try { + return Encoding.GetEncoding (WindowsCodePage) != null; + } catch (Exception) { + return false; + } + } + } + + protected override void Init () + { + EncodingWindows = Encoding.GetEncoding (WindowsCodePage); + table = new byte[LAST][]; + table [0] = errorTable; + for (int i = 1; i < LAST; i++) + table [i] = new byte[(int)byte.MaxValue + 1]; + + for (int i = 0x00; i <= 0xFF; i++) { + table [Valid] [i] = Valid; + } + table [Valid] [0x81] = Error; + table [Valid] [0x8D] = Error; + table [Valid] [0x8F] = Error; + table [Valid] [0x90] = Error; + table [Valid] [0x9D] = Error; + } + } + + /// <summary> + /// Code page 858 supports old DOS style files extended with the euro sign. + /// </summary> + class CodePage858Verifier : Verifier + { + const byte Valid = 1; + const byte LAST = 2; + static byte[][] table; + static Encoding EncodingCp858; + + public override byte InitalState { get { return Valid; } } + + public override Encoding Encoding { get { return EncodingCp858; } } + + public override byte[][] StateTable { get { return table; } } + + public override bool IsSupported { + get { + try { + return Encoding.GetEncoding (858) != null; + } catch (Exception) { + return false; + } + } + } + + protected override void Init () + { + EncodingCp858 = Encoding.GetEncoding (858); + table = new byte[LAST][]; + table [0] = errorTable; + for (int i = 1; i < LAST; i++) + table [i] = new byte[(int)byte.MaxValue + 1]; + + for (int i = 0x20; i <= 0xFF; i++) { + table [Valid] [i] = Valid; + } + } + } + + /// <summary> + /// Try to detect chinese encoding. + /// </summary> + class GB18030CodePageVerifier : Verifier + { + const byte Valid = 1; + const byte Second = 2; + const byte Third = 3; + const byte Fourth = 4; + const byte NotValid = 5; + + const byte LAST = 6; + static byte[][] table; + static Encoding EncodingWindows; + + public override byte InitalState { get { return NotValid; } } + + public override Encoding Encoding { get { return EncodingWindows; } } + + public override byte[][] StateTable { get { return table; } } + + public override bool IsEncodingValid (byte state) + { + return state == Valid; + } + + int WindowsCodePage { + get { + return 54936; + } + } + + public override bool IsSupported { + get { + try { + return Encoding.GetEncoding (WindowsCodePage) != null; + } catch (Exception) { + return false; + } + } + } + + protected override void Init () + { + EncodingWindows = Encoding.GetEncoding (WindowsCodePage); + table = new byte[LAST][]; + table [0] = errorTable; + for (int i = 1; i < LAST; i++) + table [i] = new byte[(int)byte.MaxValue + 1]; + + for (int i = 0x00; i <= 0x80; i++) + table [Valid] [i] = Valid; + for (int i = 0x81; i <= 0xFE; i++) + table [Valid] [i] = Second; + table [Valid] [0xFF] = Error; + + // need to encounter a multi byte sequence first. + for (int i = 0x00; i <= 0x80; i++) + table [NotValid] [i] = NotValid; + for (int i = 0x81; i <= 0xFE; i++) + table [NotValid] [i] = Second; + table [NotValid] [0xFF] = Error; + + for (int i = 0x00; i <= 0xFF; i++) + table [Second] [i] = Error; + for (int i = 0x40; i <= 0xFE; i++) + table [Second] [i] = Valid; + for (int i = 0x30; i <= 0x39; i++) + table [Second] [i] = Third; + + for (int i = 0x00; i <= 0xFF; i++) + table [Third] [i] = Error; + for (int i = 0x81; i <= 0xFE; i++) + table [Third] [i] = Fourth; + + for (int i = 0x00; i <= 0xFF; i++) + table [Fourth] [i] = Error; + for (int i = 0x30; i <= 0x39; i++) + table [Fourth] [i] = Valid; + } + } + #endregion + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextSourceVersionProvider.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextSourceVersionProvider.cs new file mode 100644 index 0000000000..3e2cae228b --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextSourceVersionProvider.cs @@ -0,0 +1,136 @@ +// +// TextSourceVersionProvider.cs +// +// Taken from NRefactory 5. +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2014 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.Linq; + +namespace MonoDevelop.Core.Text +{ + /// <summary> + /// Provides ITextSourceVersion instances. + /// </summary> + public class TextSourceVersionProvider + { + Version currentVersion; + + public TextSourceVersionProvider () + { + this.currentVersion = new Version (this); + } + + /// <summary> + /// Gets the current version. + /// </summary> + public ITextSourceVersion CurrentVersion { + get { return currentVersion; } + } + + /// <summary> + /// Replaces the current version with a new version. + /// </summary> + /// <param name="change">Change from current version to new version</param> + public void AppendChange (TextChangeEventArgs change) + { + if (change == null) + throw new ArgumentNullException ("change"); + currentVersion.change = change; + currentVersion.next = new Version (currentVersion); + currentVersion = currentVersion.next; + } + + sealed class Version : ITextSourceVersion + { + + // Reference back to the provider. + // Used to determine if two checkpoints belong to the same document. + readonly TextSourceVersionProvider provider; + // ID used for CompareAge() + readonly int id; + + // the change from this version to the next version + internal TextChangeEventArgs change; + internal Version next; + + internal Version (TextSourceVersionProvider provider) + { + this.provider = provider; + } + + internal Version (Version prev) + { + this.provider = prev.provider; + this.id = unchecked(prev.id + 1); + } + + public bool BelongsToSameDocumentAs (ITextSourceVersion other) + { + Version o = other as Version; + return o != null && provider == o.provider; + } + + public int CompareAge (ITextSourceVersion other) + { + if (other == null) + throw new ArgumentNullException ("other"); + var o = other as Version; + if (o == null || provider != o.provider) + throw new ArgumentException ("Versions do not belong to the same document."); + // We will allow overflows, but assume that the maximum distance between checkpoints is 2^31-1. + // This is guaranteed on x86 because so many checkpoints don't fit into memory. + return Math.Sign (unchecked(this.id - o.id)); + } + + public IEnumerable<TextChangeEventArgs> GetChangesTo (ITextSourceVersion other) + { + int result = CompareAge (other); + Version o = (Version)other; + if (result < 0) + return GetForwardChanges (o); + else if (result > 0) + return o.GetForwardChanges (this).Reverse ().Select (change => change.Invert ()); + return Enumerable.Empty<TextChangeEventArgs> (); + } + + IEnumerable<TextChangeEventArgs> GetForwardChanges (Version other) + { + for (Version node = this; node != other; node = node.next) { + yield return node.change; + } + } + + public int MoveOffsetTo (ITextSourceVersion other, int oldOffset) + { + int offset = oldOffset; + foreach (var e in GetChangesTo(other)) { + offset = e.GetNewOffset (offset); + } + return offset; + } + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/UnicodeNewLine.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/UnicodeNewLine.cs new file mode 100644 index 0000000000..0f371c2f32 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/UnicodeNewLine.cs @@ -0,0 +1,366 @@ +// +// UnicodeNewLine.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2014 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; + +namespace MonoDevelop.Core.Text +{ + public enum UnicodeNewline + { + Unknown, + + /// <summary> + /// Line Feed, U+000A + /// </summary> + LF = 0x0A, + + + CRLF = 0x0D0A, + + /// <summary> + /// Carriage Return, U+000D + /// </summary> + CR = 0x0D, + + /// <summary> + /// Next Line, U+0085 + /// </summary> + NEL = 0x85, + + /// <summary> + /// Vertical Tab, U+000B + /// </summary> + VT = 0x0B, + + /// <summary> + /// Form Feed, U+000C + /// </summary> + FF = 0x0C, + + /// <summary> + /// Line Separator, U+2028 + /// </summary> + LS = 0x2028, + + /// <summary> + /// Paragraph Separator, U+2029 + /// </summary> + PS = 0x2029 + } + + + /// <summary> + /// Defines unicode new lines according to Unicode Technical Report #13 + /// http://www.unicode.org/standard/reports/tr13/tr13-5.html + /// </summary> + public static class NewLine + { + /// <summary> + /// Carriage Return, U+000D + /// </summary> + public const char CR = (char)0x0D; + + /// <summary> + /// Line Feed, U+000A + /// </summary> + public const char LF = (char)0x0A; + + /// <summary> + /// Next Line, U+0085 + /// </summary> + public const char NEL = (char)0x85; + + /// <summary> + /// Vertical Tab, U+000B + /// </summary> + public const char VT = (char)0x0B; + + /// <summary> + /// Form Feed, U+000C + /// </summary> + public const char FF = (char)0x0C; + + /// <summary> + /// Line Separator, U+2028 + /// </summary> + public const char LS = (char)0x2028; + + /// <summary> + /// Paragraph Separator, U+2029 + /// </summary> + public const char PS = (char)0x2029; + + /// <summary> + /// Determines if a char is a new line delimiter. + /// </summary> + /// <returns>0 == no new line, otherwise it returns either 1 or 2 depending of the length of the delimiter.</returns> + /// <param name="curChar">The current character.</param> + /// <param name="nextChar">A callback getting the next character (may be null).</param> + public static int GetDelimiterLength (char curChar, Func<char> nextChar = null) + { + if (curChar == CR) { + if (nextChar != null && nextChar () == LF) + return 2; + return 1; + } + + if (curChar == LF || curChar == NEL || curChar == VT || curChar == FF || curChar == LS || curChar == PS) + return 1; + return 0; + } + + /// <summary> + /// Determines if a char is a new line delimiter. + /// </summary> + /// <returns>0 == no new line, otherwise it returns either 1 or 2 depending of the length of the delimiter.</returns> + /// <param name="curChar">The current character.</param> + /// <param name="nextChar">The next character (if != LF then length will always be 0 or 1).</param> + public static int GetDelimiterLength (char curChar, char nextChar) + { + if (curChar == CR) { + if (nextChar == LF) + return 2; + return 1; + } + + if (curChar == LF || curChar == NEL || curChar == VT || curChar == FF || curChar == LS || curChar == PS) + return 1; + return 0; + } + + + /// <summary> + /// Determines if a char is a new line delimiter. + /// </summary> + /// <returns>0 == no new line, otherwise it returns either 1 or 2 depending of the length of the delimiter.</returns> + /// <param name="curChar">The current character.</param> + /// <param name = "length">The length of the delimiter</param> + /// <param name = "type">The type of the delimiter</param> + /// <param name="nextChar">A callback getting the next character (may be null).</param> + public static bool TryGetDelimiterLengthAndType (char curChar, out int length, out UnicodeNewline type, Func<char> nextChar = null) + { + if (curChar == CR) { + if (nextChar != null && nextChar () == LF) { + length = 2; + type = UnicodeNewline.CRLF; + } else { + length = 1; + type = UnicodeNewline.CR; + + } + return true; + } + + switch (curChar) { + case LF: + type = UnicodeNewline.LF; + length = 1; + return true; + case NEL: + type = UnicodeNewline.NEL; + length = 1; + return true; + case VT: + type = UnicodeNewline.VT; + length = 1; + return true; + case FF: + type = UnicodeNewline.FF; + length = 1; + return true; + case LS: + type = UnicodeNewline.LS; + length = 1; + return true; + case PS: + type = UnicodeNewline.PS; + length = 1; + return true; + } + length = -1; + type = UnicodeNewline.Unknown; + return false; + } + + /// <summary> + /// Determines if a char is a new line delimiter. + /// </summary> + /// <returns>0 == no new line, otherwise it returns either 1 or 2 depending of the length of the delimiter.</returns> + /// <param name="curChar">The current character.</param> + /// <param name = "length">The length of the delimiter</param> + /// <param name = "type">The type of the delimiter</param> + /// <param name="nextChar">The next character (if != LF then length will always be 0 or 1).</param> + public static bool TryGetDelimiterLengthAndType (char curChar, out int length, out UnicodeNewline type, char nextChar) + { + if (curChar == CR) { + if (nextChar == LF) { + length = 2; + type = UnicodeNewline.CRLF; + } else { + length = 1; + type = UnicodeNewline.CR; + + } + return true; + } + + switch (curChar) { + case LF: + type = UnicodeNewline.LF; + length = 1; + return true; + case NEL: + type = UnicodeNewline.NEL; + length = 1; + return true; + case VT: + type = UnicodeNewline.VT; + length = 1; + return true; + case FF: + type = UnicodeNewline.FF; + length = 1; + return true; + case LS: + type = UnicodeNewline.LS; + length = 1; + return true; + case PS: + type = UnicodeNewline.PS; + length = 1; + return true; + } + length = -1; + type = UnicodeNewline.Unknown; + return false; + } + + /// <summary> + /// Gets the new line type of a given char/next char. + /// </summary> + /// <returns>0 == no new line, otherwise it returns either 1 or 2 depending of the length of the delimiter.</returns> + /// <param name="curChar">The current character.</param> + /// <param name="nextChar">A callback getting the next character (may be null).</param> + public static UnicodeNewline GetDelimiterType (char curChar, Func<char> nextChar = null) + { + switch (curChar) { + case CR: + if (nextChar != null && nextChar () == LF) + return UnicodeNewline.CRLF; + return UnicodeNewline.CR; + case LF: + return UnicodeNewline.LF; + case NEL: + return UnicodeNewline.NEL; + case VT: + return UnicodeNewline.VT; + case FF: + return UnicodeNewline.FF; + case LS: + return UnicodeNewline.LS; + case PS: + return UnicodeNewline.PS; + } + return UnicodeNewline.Unknown; + } + + /// <summary> + /// Gets the new line type of a given char/next char. + /// </summary> + /// <returns>0 == no new line, otherwise it returns either 1 or 2 depending of the length of the delimiter.</returns> + /// <param name="curChar">The current character.</param> + /// <param name="nextChar">The next character (if != LF then length will always be 0 or 1).</param> + public static UnicodeNewline GetDelimiterType (char curChar, char nextChar) + { + switch (curChar) { + case CR: + if (nextChar == LF) + return UnicodeNewline.CRLF; + return UnicodeNewline.CR; + case LF: + return UnicodeNewline.LF; + case NEL: + return UnicodeNewline.NEL; + case VT: + return UnicodeNewline.VT; + case FF: + return UnicodeNewline.FF; + case LS: + return UnicodeNewline.LS; + case PS: + return UnicodeNewline.PS; + } + return UnicodeNewline.Unknown; + } + + /// <summary> + /// Determines if a char is a new line delimiter. + /// + /// Note that the only 2 char wide new line is CR LF and both chars are new line + /// chars on their own. For most cases GetDelimiterLength is the better choice. + /// </summary> + public static bool IsNewLine (char ch) + { + return + ch == NewLine.CR || + ch == NewLine.LF || + ch == NewLine.NEL || + ch == NewLine.VT || + ch == NewLine.FF || + ch == NewLine.LS || + ch == NewLine.PS; + } + + /// <summary> + /// Gets the new line as a string. + /// </summary> + public static string GetString (UnicodeNewline newLine) + { + switch (newLine) { + case UnicodeNewline.Unknown: + return ""; + case UnicodeNewline.LF: + return "\n"; + case UnicodeNewline.CRLF: + return "\r\n"; + case UnicodeNewline.CR: + return "\r"; + case UnicodeNewline.NEL: + return "\u0085"; + case UnicodeNewline.VT: + return "\u000B"; + case UnicodeNewline.FF: + return "\u000C"; + case UnicodeNewline.LS: + return "\u2028"; + case UnicodeNewline.PS: + return "\u2029"; + default: + throw new ArgumentOutOfRangeException (); + } + } + } +} + diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj index d980d4c1ce..411f70c72e 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj @@ -98,6 +98,18 @@ </Reference> <Reference Include="System.Xml.Linq" /> <Reference Include="System.Web" /> + <Reference Include="System.Collections.Immutable"> + <HintPath>..\..\..\packages\System.Collections.Immutable.1.1.33-beta\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll</HintPath> + </Reference> + <Reference Include="System.Reflection.Metadata"> + <HintPath>..\..\..\packages\System.Reflection.Metadata.1.0.18-beta\lib\portable-net45+win8\System.Reflection.Metadata.dll</HintPath> + </Reference> + <Reference Include="Microsoft.CodeAnalysis.Desktop"> + <HintPath>..\..\..\packages\Microsoft.CodeAnalysis.Common.1.0.0-rc1\lib\net45\Microsoft.CodeAnalysis.Desktop.dll</HintPath> + </Reference> + <Reference Include="Microsoft.CodeAnalysis"> + <HintPath>..\..\..\packages\Microsoft.CodeAnalysis.Common.1.0.0-rc1\lib\net45\Microsoft.CodeAnalysis.dll</HintPath> + </Reference> <Reference Include="System.ServiceModel" /> <Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.Build.Engine" /> @@ -346,9 +358,7 @@ <Compile Include="MonoDevelop.Projects.Policies\PolicyContainer.cs" /> <Compile Include="MonoDevelop.Projects.Policies\ScopedPolicy.cs" /> <Compile Include="MonoDevelop.Projects.Policies\PolicyDictionary.cs" /> - <Compile Include="MonoDevelop.Projects.Text\IEditableTextFile.cs" /> <Compile Include="MonoDevelop.Projects.Text\ITextFile.cs" /> - <Compile Include="MonoDevelop.Projects.Text\ITextFileProvider.cs" /> <Compile Include="MonoDevelop.Projects.Text\TextFile.cs" /> <Compile Include="MonoDevelop.Projects.Text\TextEncoding.cs" /> <Compile Include="MonoDevelop.Projects.Text\TextFileReader.cs" /> @@ -397,7 +407,6 @@ <Compile Include="MonoDevelop.Core\LinuxSystemInformation.cs" /> <Compile Include="MonoDevelop.Core\UnixSystemInformation.cs" /> <Compile Include="MonoDevelop.Core\ISystemInformationProvider.cs" /> - <Compile Include="MonoDevelop.Core.FileSystem\UnixFileSystemExtension.cs" /> <Compile Include="MonoDevelop.Core.LogReporting\CrashEventArgs.cs" /> <Compile Include="MonoDevelop.Core.LogReporting\CrashMonitor.cs" /> <Compile Include="MonoDevelop.Core.LogReporting\ICrashMonitor.cs" /> @@ -438,10 +447,18 @@ <Compile Include="MonoDevelop.Core.Web\STSAuthHelper.cs" /> <Compile Include="MonoDevelop.Core.Web\WIFTypeProvider.cs" /> <Compile Include="MonoDevelop.Core.Web\CredentialProviderExtensions.cs" /> + <Compile Include="MonoDevelop.Core.Text\ISegment.cs" /> + <Compile Include="MonoDevelop.Core.Text\UnicodeNewLine.cs" /> + <Compile Include="MonoDevelop.Core.Text\ITextSource.cs" /> + <Compile Include="MonoDevelop.Core.Text\TextChangeEventArgs.cs" /> + <Compile Include="MonoDevelop.Core.Text\StringTextSource.cs" /> + <Compile Include="MonoDevelop.Core.Text\TextFileUtility.cs" /> + <Compile Include="MonoDevelop.Core.Text\ITextSourceVersion.cs" /> <Compile Include="MonoDevelop.Core.Execution\IDebugConsole.cs" /> <Compile Include="MonoDevelop.Projects\IDotNetFileContainer.cs" /> <Compile Include="MonoDevelop.Projects.Formats.MSBuild\IMSBuildGlobalPropertyProvider.cs" /> <Compile Include="MonoDevelop.Projects\DotNetProjectImport.cs" /> + <Compile Include="MonoDevelop.Core.Text\TextSourceVersionProvider.cs" /> <Compile Include="MonoDevelop.Core.LogReporting\CrashReporter.cs" /> <Compile Include="MonoDevelop.Core.Instrumentation\InstrumentationConsumer.cs" /> <Compile Include="MonoDevelop.Projects\DotNetProjectExtension.cs" /> @@ -572,14 +589,6 @@ <Project>{A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}</Project> <Name>Newtonsoft.Json</Name> </ProjectReference> - <ProjectReference Include="..\..\..\external\nrefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj"> - <Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project> - <Name>ICSharpCode.NRefactory</Name> - </ProjectReference> - <ProjectReference Include="..\..\..\external\nrefactory\ICSharpCode.NRefactory.CSharp\ICSharpCode.NRefactory.CSharp.csproj"> - <Project>{53DCA265-3C3C-42F9-B647-F72BA678122B}</Project> - <Name>ICSharpCode.NRefactory.CSharp</Name> - </ProjectReference> <ProjectReference Include="..\..\..\external\mono-addins\Mono.Addins\Mono.Addins.csproj"> <Project>{91DD5A2D-9FE3-4C3C-9253-876141874DAD}</Project> <Name>Mono.Addins</Name> @@ -600,10 +609,6 @@ <Project>{63E6915C-7EA4-4D76-AB28-0D7191EEA626}</Project> <Name>Mono.Cecil.Pdb</Name> </ProjectReference> - <ProjectReference Include="..\..\..\external\nrefactory\ICSharpCode.NRefactory.Cecil\ICSharpCode.NRefactory.Cecil.csproj"> - <Project>{2B8F4F83-C2B3-4E84-A27B-8DEE1BE0E006}</Project> - <Name>ICSharpCode.NRefactory.Cecil</Name> - </ProjectReference> </ItemGroup> <ItemGroup> <Content Include="MonoDevelop.Core.dll.config"> diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FilePath.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FilePath.cs index c972e9cce6..5718176fcd 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FilePath.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FilePath.cs @@ -28,14 +28,15 @@ using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; +using System.Runtime.InteropServices; namespace MonoDevelop.Core { [Serializable]
public struct FilePath: IComparable<FilePath>, IComparable, IEquatable<FilePath>
{ - static readonly StringComparer PathComparer = (Platform.IsWindows || Platform.IsMac) ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal; - static readonly StringComparison PathComparison = (Platform.IsWindows || Platform.IsMac) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + public static readonly StringComparer PathComparer = (Platform.IsWindows || Platform.IsMac) ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal; + public static readonly StringComparison PathComparison = (Platform.IsWindows || Platform.IsMac) ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; readonly string fileName;
@@ -64,6 +65,28 @@ namespace MonoDevelop.Core public bool IsEmpty {
get { return fileName != null && fileName.Length == 0; }
+ } + + const int PATHMAX = 4096 + 1; + + [DllImport ("libc")] + static extern IntPtr realpath (string path, IntPtr buffer); + + public FilePath ResolveLinks () + { + if (Platform.IsWindows) { + return Path.GetFullPath (this); + } + + IntPtr buffer = IntPtr.Zero; + try { + buffer = Marshal.AllocHGlobal (PATHMAX); + var result = realpath (this, buffer); + return result == IntPtr.Zero ? "" : Marshal.PtrToStringAuto (buffer); + } finally { + if (buffer != IntPtr.Zero) + Marshal.FreeHGlobal (buffer); + } }
public FilePath FullPath {
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs index 96d24ff503..6968d7b776 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/FileService.cs @@ -50,7 +50,7 @@ namespace MonoDevelop.Core static FileServiceErrorHandler errorHandler;
static FileSystemExtension fileSystemChain;
- static readonly FileSystemExtension defaultExtension = Platform.IsWindows ? new DefaultFileSystemExtension () : new UnixFileSystemExtension () ;
+ static readonly FileSystemExtension defaultExtension = new DefaultFileSystemExtension ();
static readonly EventQueue eventQueue = new EventQueue ();
@@ -90,17 +90,6 @@ namespace MonoDevelop.Core }
}
- public static FilePath ResolveFullPath (FilePath path)
- {
- try {
- return GetFileSystemForPath (path, false).ResolveFullPath (path);
- } catch (Exception e) {
- if (!HandleError (GettextCatalog.GetString ("Can't resolve full path {0}", path), e))
- throw;
- return FilePath.Empty;
- }
- }
-
public static void DeleteFile (string fileName)
{
Debug.Assert (!String.IsNullOrEmpty (fileName));
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Properties.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Properties.cs index 2c35989714..22f5906ee3 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Properties.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Properties.cs @@ -28,7 +28,7 @@ using System; using System.Collections.Generic; -using System.ComponentModel;
+using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Reflection; @@ -37,17 +37,18 @@ using System.Xml; using System.Xml.Serialization; using MonoDevelop.Core; +using System.Collections.Immutable; namespace MonoDevelop.Core { public class Properties : ICustomXmlSerializer { - Dictionary<string, object> properties = new Dictionary<string, object> (); - Dictionary<string, object> defaultValues = new Dictionary<string, object> (); - Dictionary<Type, TypeConverter> cachedConverters = new Dictionary<Type, TypeConverter> (); + ImmutableDictionary<string, object> properties = ImmutableDictionary<string, object>.Empty; + ImmutableDictionary<string, object> defaultValues = ImmutableDictionary<string, object>.Empty; + ImmutableDictionary<Type, TypeConverter> cachedConverters = ImmutableDictionary<Type, TypeConverter>.Empty; Dictionary<string,EventHandler<PropertyChangedEventArgs>> propertyListeners; - public ICollection<string> Keys { + public IEnumerable<string> Keys { get { return properties.Keys; } @@ -66,19 +67,19 @@ namespace MonoDevelop.Core return (T) o; TypeConverter converter = GetConverter (typeof(T)); -
- if (o is string) {
- try {
- return (T)converter.ConvertFromInvariantString (o.ToString ());
- } catch (Exception) {
- return default(T);
+ + if (o is string) { + try { + return (T)converter.ConvertFromInvariantString (o.ToString ()); + } catch (Exception) { + return default(T); } } - try {
- return (T)converter.ConvertFrom (o);
- } catch (Exception) {
- return default(T);
+ try { + return (T)converter.ConvertFrom (o); + } catch (Exception) { + return default(T); } } @@ -87,26 +88,26 @@ namespace MonoDevelop.Core if (o == null) return null; TypeConverter converter = GetConverter (o.GetType ()); - return converter.ConvertToInvariantString (o);
+ return converter.ConvertToInvariantString (o); } TypeConverter GetConverter (Type type) { TypeConverter converter; - if (!cachedConverters.TryGetValue (type, out converter)) {
+ if (!cachedConverters.TryGetValue (type, out converter)) { converter = TypeDescriptor.GetConverter (type); - cachedConverters [type] = converter; + cachedConverters = cachedConverters.SetItem (type, converter); } return converter; } public T Get<T> (string property, T defaultValue) { - defaultValues[property] = defaultValue; + defaultValues = defaultValues.SetItem (property, defaultValue); object val; if (GetPropertyValue<T> (property, out val)) return Convert<T> (val); - properties[property] = defaultValue; + properties = properties.SetItem (property, defaultValue); return defaultValue; } @@ -127,7 +128,7 @@ namespace MonoDevelop.Core // Deserialize the data and store it in the dictionary, so // following calls return the same object val = ((LazyXmlDeserializer)val).Deserialize<T> (); - properties[property] = val; + properties = properties.SetItem (property, val); } return true; } else { @@ -154,7 +155,7 @@ namespace MonoDevelop.Core //avoid emitting the event if not necessary if (val.Equals (old)) return; - properties[key] = val; + properties = properties.SetItem (key, val); if (!val.GetType ().IsClass ||(val is string)) { if (defaultValues.ContainsKey (key)) { if (defaultValues[key] == val) @@ -239,14 +240,14 @@ namespace MonoDevelop.Core } //write out the new state to a temp file - try {
- using (XmlTextWriter writer = new XmlTextWriter (tempFileName, System.Text.Encoding.UTF8)) {
- writer.Formatting = Formatting.Indented;
- writer.WriteStartElement (PropertiesRootNode);
- writer.WriteAttributeString (PropertiesVersionAttribute, PropertiesVersion);
- Write (writer, false);
- writer.WriteEndElement (); // PropertiesRootNode
- }
+ try { + using (XmlTextWriter writer = new XmlTextWriter (tempFileName, System.Text.Encoding.UTF8)) { + writer.Formatting = Formatting.Indented; + writer.WriteStartElement (PropertiesRootNode); + writer.WriteAttributeString (PropertiesVersionAttribute, PropertiesVersion); + Write (writer, false); + writer.WriteEndElement (); // PropertiesRootNode + } //write was successful (no exception) //so move the file to the real location, overwriting the old file @@ -355,7 +356,7 @@ namespace MonoDevelop.Core public Properties Clone () { Properties result = new Properties (); - result.properties = new Dictionary<string, object> (properties); + result.properties = properties; return result; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/DocGenerator.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/DocGenerator.cs index 7d5a752b09..94622ce476 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/DocGenerator.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/DocGenerator.cs @@ -26,7 +26,7 @@ using System; using MonoDevelop.Core; using Mono.Addins; -using ICSharpCode.NRefactory.TypeSystem; +using Microsoft.CodeAnalysis; namespace MonoDevelop.Projects.Text { @@ -37,7 +37,7 @@ namespace MonoDevelop.Projects.Text private set; } - public abstract string GenerateDocumentation (IMember member, string linePrefix); + public abstract string GenerateDocumentation (ISymbol member, string linePrefix); static DocGenerator () { diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/IEditableTextFile.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/IEditableTextFile.cs deleted file mode 100644 index fa9a33ff06..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/IEditableTextFile.cs +++ /dev/null @@ -1,47 +0,0 @@ -// -// IEditableTextFile.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (C) 2005 Novell, Inc (http://www.novell.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; - -namespace MonoDevelop.Projects.Text -{ - public interface IEditableTextFile: ITextFile - { - new string Text { - get; - set; - } - - /// <returns> - /// The length of the inserted text. The real text may differ in lenth because - /// of some conversions (tabs -> spaces, different line ends etc.) - /// </returns> - int InsertText (int position, string text); - void DeleteText (int position, int length); - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/ITextFileProvider.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/ITextFileProvider.cs deleted file mode 100644 index 47afc17755..0000000000 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/ITextFileProvider.cs +++ /dev/null @@ -1,38 +0,0 @@ -// -// ITextFileProvider.cs -// -// Author: -// Lluis Sanchez Gual -// -// Copyright (C) 2005 Novell, Inc (http://www.novell.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 MonoDevelop.Core; - -namespace MonoDevelop.Projects.Text -{ - public interface ITextFileProvider - {
- IEditableTextFile GetEditableTextFile (FilePath filePath); - } -} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextFile.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextFile.cs index 85f4dd8b26..7aa7443e11 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextFile.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextFile.cs @@ -39,7 +39,7 @@ using MonoDevelop.Projects.Utility; namespace MonoDevelop.Projects.Text { - public class TextFile: IEditableTextFile + public class TextFile: ITextFile { const string LIBGLIB = "libglib-2.0-0.dll"; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetCompilerParameters.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetCompilerParameters.cs index 48cb680170..79cb567ff8 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetCompilerParameters.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetCompilerParameters.cs @@ -72,5 +72,8 @@ namespace MonoDevelop.Projects public virtual bool NoStdLib { get; set; } public virtual string DebugType { get { return ""; } set {} } + + public abstract Microsoft.CodeAnalysis.CompilationOptions CreateCompilationOptions (); + public abstract Microsoft.CodeAnalysis.ParseOptions CreateParseOptions (); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/HelpService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/HelpService.cs index 55148f5392..c5bf38ffcb 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/HelpService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/HelpService.cs @@ -33,11 +33,6 @@ using Mono.Addins; using System.IO; using System.Collections.Generic; using MonoDevelop.Projects.Extensions; -using ICSharpCode.NRefactory.TypeSystem; -using System.Text; -using System.Xml; -using ICSharpCode.NRefactory.Semantics; -using ICSharpCode.NRefactory.Documentation; namespace MonoDevelop.Projects { @@ -135,172 +130,12 @@ namespace MonoDevelop.Projects //note: this method is very careful to check that the generated URLs exist in MonoDoc //because if we send nonexistent URLS to MonoDoc, it shows empty pages - public static string GetMonoDocHelpUrl (ResolveResult result) + public static string GetMonoDocHelpUrl (Microsoft.CodeAnalysis.ISymbol result) { if (result == null) return null; - -// if (result is AggregatedResolveResult) -// result = ((AggregatedResolveResult)result).PrimaryResult; - - - if (result is NamespaceResolveResult) { - string namespc = ((NamespaceResolveResult)result).NamespaceName; - //verify that the namespace exists in the help tree - //FIXME: GetHelpXml doesn't seem to work for namespaces, so forced to do full render - Monodoc.Node dummy; - if (!String.IsNullOrEmpty (namespc) && HelpTree != null && HelpTree.RenderUrl ("N:" + namespc, out dummy) != null) - return "N:" + namespc; - else - return null; - } - - IMember member = null; -// if (result is MethodGroupResolveResult) -// member = ((MethodGroupResolveResult)result).Methods.FirstOrDefault (); -// else - if (result is MemberResolveResult) - member = ((MemberResolveResult)result).Member; - - if (member != null && member.GetMonodocDocumentation () != null) - return member.GetIdString (); - - var type = result.Type; - if (type != null && !String.IsNullOrEmpty (type.FullName)) { - string t = "T:" + type.FullName; - try { - var tree = HelpTree; - if (tree != null && tree.GetHelpXml (t) != null) - return t; - } catch (Exception) { - return null; - } - } - - return null; - } - } - - public static class HelpExtension - { - static void AppendTypeReference (StringBuilder result, ITypeReference type) - { - if (type is ArrayTypeReference) { - var array = (ArrayTypeReference)type; - AppendTypeReference (result, array.ElementType); - result.Append ("["); - result.Append (new string (',', array.Dimensions)); - result.Append ("]"); - return; - } - - if (type is PointerTypeReference) { - var ptr = (PointerTypeReference)type; - AppendTypeReference (result, ptr.ElementType); - result.Append ("*"); - return; - } - - if (type is IType) - result.Append (((IType)type).FullName); + return result.GetDocumentationCommentId (); } - - - static void AppendHelpParameterList (StringBuilder result, IList<IParameter> parameters) - { - result.Append ('('); - if (parameters != null) { - for (int i = 0; i < parameters.Count; i++) { - if (i > 0) - result.Append (','); - var p = parameters [i]; - if (p == null) - continue; - if (p.IsRef || p.IsOut) - result.Append ("&"); - AppendTypeReference (result, p.Type.ToTypeReference ()); - } - } - result.Append (')'); - } - - static void AppendHelpParameterList (StringBuilder result, IList<IUnresolvedParameter> parameters) - { - result.Append ('('); - if (parameters != null) { - for (int i = 0; i < parameters.Count; i++) { - if (i > 0) - result.Append (','); - var p = parameters [i]; - if (p == null) - continue; - if (p.IsRef || p.IsOut) - result.Append ("&"); - AppendTypeReference (result, p.Type); - } - } - result.Append (')'); - } - - static XmlNode FindMatch (IMethod method, XmlNodeList nodes) - { - foreach (XmlNode node in nodes) { - XmlNodeList paramList = node.SelectNodes ("Parameters/*"); - if (method.Parameters.Count == 0 && paramList.Count == 0) - return node; - if (method.Parameters.Count != paramList.Count) - continue; - -/* bool matched = true; - for (int i = 0; i < p.Count; i++) { - if (p [i].ReturnType.FullName != paramList [i].Attributes ["Type"].Value) { - matched = false; - break; - } - } - if (matched)*/ - return node; - } - return null; - } - - public static XmlNode GetMonodocDocumentation (this IEntity member) - { - if (member.SymbolKind == SymbolKind.TypeDefinition) { - var helpXml = HelpService.HelpTree != null ? HelpService.HelpTree.GetHelpXml (member.GetIdString ()) : null; - if (helpXml == null) - return null; - return helpXml.SelectSingleNode ("/Type/Docs"); - } - - var declaringXml = HelpService.HelpTree != null && member.DeclaringTypeDefinition != null ? HelpService.HelpTree.GetHelpXml (member.DeclaringTypeDefinition.GetIdString ()) : null; - if (declaringXml == null) - return null; - - switch (member.SymbolKind) { - case SymbolKind.Method: { - var nodes = declaringXml.SelectNodes ("/Type/Members/Member[@MemberName='" + member.Name + "']"); - XmlNode node = nodes.Count == 1 ? nodes [0] : FindMatch ((IMethod)member, nodes); - if (node != null) { - System.Xml.XmlNode result = node.SelectSingleNode ("Docs"); - return result; - } - return null; - } - case SymbolKind.Constructor: { - var nodes = declaringXml.SelectNodes ("/Type/Members/Member[@MemberName='.ctor']"); - XmlNode node = nodes.Count == 1 ? nodes [0] : FindMatch ((IMethod)member, nodes); - if (node != null) { - System.Xml.XmlNode result = node.SelectSingleNode ("Docs"); - return result; - } - return null; - } - default: - return declaringXml.SelectSingleNode ("/Type/Members/Member[@MemberName='" + member.Name + "']/Docs"); - } - } - } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs index 20dffa193b..12ce44ff8c 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/ProjectService.cs @@ -35,6 +35,8 @@ using MonoDevelop.Core.Serialization; using MonoDevelop.Core; using Mono.Addins; +using MonoDevelop.Core.ProgressMonitoring; +using MonoDevelop.Core.Execution; using MonoDevelop.Core.Assemblies; using MonoDevelop.Core.Instrumentation; using MonoDevelop.Projects.Extensions; @@ -177,7 +179,7 @@ namespace MonoDevelop.Projects return Runtime.RunInMainThread (async delegate { if (!File.Exists (file)) throw new IOException (GettextCatalog.GetString ("File not found: {0}", file)); - string fullpath = FileService.ResolveFullPath (file).FullPath; + string fullpath = file.ResolveLinks ().FullPath; using (Counters.ReadWorkspaceItem.BeginTiming ("Read solution " + file)) { fullpath = GetTargetFile (fullpath); WorkspaceItem item = await GetExtensionChain ().LoadWorkspaceItem (monitor, fullpath) as WorkspaceItem; diff --git a/main/src/core/MonoDevelop.Core/packages.config b/main/src/core/MonoDevelop.Core/packages.config index 591f985ff1..6cc5b68cc1 100644 --- a/main/src/core/MonoDevelop.Core/packages.config +++ b/main/src/core/MonoDevelop.Core/packages.config @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="Microsoft.Bcl.Immutable" version="1.0.34" targetFramework="net45" />
+ <package id="Microsoft.CodeAnalysis.Common" version="1.0.0-rc1" targetFramework="net45" />
+ <package id="System.Collections.Immutable" version="1.1.33-beta" targetFramework="net45" />
+ <package id="System.Reflection.Metadata" version="1.0.18-beta" targetFramework="net45" />
</packages>
\ No newline at end of file |