100 changed files with 1892 additions and 1832 deletions
@ -1,160 +0,0 @@
@@ -1,160 +0,0 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Collections.ObjectModel; |
||||
|
||||
namespace ICSharpCode.CppBinding.Project |
||||
{ |
||||
public interface IMultiDictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>> |
||||
{ |
||||
void Add(TKey key, TValue value); |
||||
bool Contains(TKey key, TValue value); |
||||
bool ContainsKey(TKey key); |
||||
bool Remove(TKey key, TValue value); |
||||
IList<TValue> this[TKey key] { get; } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// A dictionary that allows multiple pairs with the same key.
|
||||
/// </summary>
|
||||
public class MultiDictionary<TKey, TValue> : IMultiDictionary<TKey,TValue> |
||||
{ |
||||
public MultiDictionary() |
||||
: this(new Dictionary<TKey, IList<TValue>>()) |
||||
{ } |
||||
|
||||
public MultiDictionary(IDictionary<TKey, IList<TValue>> innerDictionary) |
||||
{ |
||||
if (innerDictionary == null) |
||||
throw new ArgumentNullException("innerDictionary"); |
||||
dict = innerDictionary; |
||||
count = CountElements(dict); |
||||
} |
||||
IDictionary<TKey, IList<TValue>> dict; |
||||
int count; |
||||
|
||||
public void Add(TKey key, TValue value) |
||||
{ |
||||
IList<TValue> valueList; |
||||
if (!dict.TryGetValue(key, out valueList)) |
||||
{ |
||||
valueList = new List<TValue>(); |
||||
dict.Add(key, valueList); |
||||
} |
||||
valueList.Add(value); |
||||
count++; |
||||
} |
||||
|
||||
public bool Contains(TKey key, TValue value) |
||||
{ |
||||
IList<TValue> valueList; |
||||
if (!dict.TryGetValue(key, out valueList)) |
||||
return false; |
||||
return valueList.Contains(value); |
||||
} |
||||
|
||||
public bool ContainsKey(TKey key) |
||||
{ |
||||
return dict.ContainsKey(key); |
||||
} |
||||
|
||||
public bool Remove(TKey key, TValue value) |
||||
{ |
||||
IList<TValue> valueList; |
||||
if (!dict.TryGetValue(key, out valueList)) |
||||
return false; |
||||
return valueList.Remove(value); |
||||
} |
||||
|
||||
public IList<TValue> this[TKey key] |
||||
{ |
||||
get |
||||
{ |
||||
return new ReadOnlyCollection<TValue>(dict[key]); |
||||
} |
||||
} |
||||
|
||||
#region ICollection<KeyValuePair<TKey,TValue>> Members
|
||||
|
||||
public void Add(KeyValuePair<TKey, TValue> item) |
||||
{ |
||||
Add(item.Key, item.Value); |
||||
} |
||||
|
||||
public void Clear() |
||||
{ |
||||
dict.Clear(); |
||||
count = 0; |
||||
} |
||||
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item) |
||||
{ |
||||
return Contains(item.Key, item.Value); |
||||
} |
||||
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) |
||||
{ |
||||
if (array == null) |
||||
throw new ArgumentNullException("array"); |
||||
if (arrayIndex < 0) |
||||
throw new ArgumentOutOfRangeException("arrayIndex"); |
||||
if (array.Rank != 1) |
||||
throw new ArgumentException("Array is multidimensional", "array"); |
||||
if (arrayIndex + count >= array.Length) |
||||
throw new ArgumentException("Array is to small", "array"); |
||||
|
||||
foreach (KeyValuePair<TKey, IList<TValue>> item in dict) |
||||
foreach (TValue value in item.Value) |
||||
array[arrayIndex++] = new KeyValuePair<TKey, TValue>(item.Key, value); |
||||
} |
||||
|
||||
public int Count |
||||
{ |
||||
get { return count; } |
||||
} |
||||
|
||||
public bool IsReadOnly |
||||
{ |
||||
get { return false; } |
||||
} |
||||
|
||||
public bool Remove(KeyValuePair<TKey, TValue> item) |
||||
{ |
||||
return Remove(item.Key, item.Value); |
||||
} |
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<KeyValuePair<TKey,TValue>> Members
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() |
||||
{ |
||||
foreach (KeyValuePair<TKey, IList<TValue>> item in dict) |
||||
foreach (TValue value in item.Value) |
||||
yield return new KeyValuePair<TKey, TValue>(item.Key, value); |
||||
} |
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() |
||||
{ |
||||
return GetEnumerator(); |
||||
} |
||||
|
||||
#endregion
|
||||
|
||||
static int CountElements(IDictionary<TKey, IList<TValue>> dict) |
||||
{ |
||||
int count = 0; |
||||
foreach (KeyValuePair<TKey, IList<TValue>> item in dict) |
||||
count += item.Value.Count; |
||||
return count; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,55 @@
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Text; |
||||
using System.Threading; |
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.NRefactory.Editor; |
||||
|
||||
namespace ICSharpCode.SharpDevelop |
||||
{ |
||||
/// <summary>
|
||||
/// File service.
|
||||
/// </summary>
|
||||
public interface IFileService |
||||
{ |
||||
/// <summary>
|
||||
/// Gets the default file encoding.
|
||||
/// This property is thread-safe.
|
||||
/// </summary>
|
||||
Encoding DefaultFileEncoding { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the content of the specified file.
|
||||
/// If the file is currently open in SharpDevelop, retrieves a snapshot
|
||||
/// of the editor content.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is thread-safe. This method involves waiting for the main thread, so using it while
|
||||
/// holding a lock can lead to deadlocks.
|
||||
/// </remarks>
|
||||
ITextSource GetFileContent(FileName fileName); |
||||
|
||||
/// <inheritdoc cref="GetParseableFileContent(FileName)"/>
|
||||
ITextSource GetFileContent(string fileName); |
||||
|
||||
/// <summary>
|
||||
/// Gets the file content for a file that is currently open.
|
||||
/// Returns null if the file is not open.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is thread-safe. This method involves waiting for the main thread, so using it while
|
||||
/// holding a lock can lead to deadlocks.
|
||||
/// </remarks>
|
||||
ITextSource GetFileContentForOpenFile(FileName fileName); |
||||
|
||||
/// <summary>
|
||||
/// Gets the file content from disk, ignoring open files.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is thread-safe.
|
||||
/// </remarks>
|
||||
ITextSource GetFileContentFromDisk(FileName fileName, CancellationToken cancellationToken = default(CancellationToken)); |
||||
} |
||||
} |
@ -0,0 +1,93 @@
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.ComponentModel; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using System.Windows.Threading; |
||||
|
||||
namespace ICSharpCode.SharpDevelop |
||||
{ |
||||
/// <summary>
|
||||
/// Represents a thread running a message loop.
|
||||
/// </summary>
|
||||
public interface IMessageLoop |
||||
{ |
||||
/// <summary>
|
||||
/// Gets the thread corresponding to this message loop.
|
||||
/// </summary>
|
||||
Thread Thread { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the dispatcher for this message loop.
|
||||
/// </summary>
|
||||
Dispatcher Dispatcher { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the synchronization context corresponding to this message loop.
|
||||
/// </summary>
|
||||
SynchronizationContext SynchronizationContext { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ISynchronizeInvoke"/> implementation corresponding to this message loop.
|
||||
/// </summary>
|
||||
ISynchronizeInvoke SynchronizingObject { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets whether the current thread is different from the thread running this message loop.
|
||||
/// </summary>
|
||||
/// <remarks><c>InvokeRequired = !CheckAcess()</c></remarks>
|
||||
bool InvokeRequired { get; } |
||||
|
||||
/// <summary>
|
||||
/// Gets whether the current thread is the same as the thread running this message loop.
|
||||
/// </summary>
|
||||
/// <remarks><c>CheckAccess() = !InvokeRequired</c></remarks>
|
||||
bool CheckAccess(); |
||||
|
||||
/// <summary>
|
||||
/// Throws an exception if the current thread is different from the thread running this message loop.
|
||||
/// </summary>
|
||||
void VerifyAccess(); |
||||
|
||||
/// <summary>
|
||||
/// Invokes the specified callback on the message loop and waits for its completion.
|
||||
/// If the current thread is the thread running the message loop, executes the callback
|
||||
/// directly without pumping the message loop.
|
||||
/// </summary>
|
||||
void InvokeIfRequired(Action callback); |
||||
/// <inheritdoc see="Invoke(Action)"/>
|
||||
void InvokeIfRequired(Action callback, DispatcherPriority priority); |
||||
/// <inheritdoc see="Invoke(Action)"/>
|
||||
void InvokeIfRequired(Action callback, DispatcherPriority priority, CancellationToken cancellationToken); |
||||
|
||||
/// <summary>
|
||||
/// Invokes the specified callback, waits for its completion, and returns the result.
|
||||
/// If the current thread is the thread running the message loop, executes the callback
|
||||
/// directly without pumping the message loop.
|
||||
/// </summary>
|
||||
T InvokeIfRequired<T>(Func<T> callback); |
||||
/// <inheritdoc see="Invoke{T}(Func{T})"/>
|
||||
T InvokeIfRequired<T>(Func<T> callback, DispatcherPriority priority); |
||||
/// <inheritdoc see="Invoke{T}(Func{T})"/>
|
||||
T InvokeIfRequired<T>(Func<T> callback, DispatcherPriority priority, CancellationToken cancellationToken); |
||||
|
||||
/// <summary>
|
||||
/// Invokes the specified callback.
|
||||
/// </summary>
|
||||
/// <returns>Returns a task that is signalled when the execution of the callback is completed.</returns>
|
||||
Task InvokeAsync(Action callback); |
||||
/// <inheritdoc see="InvokeAsync(Action)"/>
|
||||
Task InvokeAsync(Action callback, DispatcherPriority priority); |
||||
/// <inheritdoc see="InvokeAsync(Action)"/>
|
||||
Task InvokeAsync(Action callback, DispatcherPriority priority, CancellationToken cancellationToken); |
||||
|
||||
/// <inheritdoc see="InvokeAsync(Action)"/>
|
||||
Task<T> InvokeAsync<T>(Func<T> callback); |
||||
/// <inheritdoc see="InvokeAsync(Action)"/>
|
||||
Task<T> InvokeAsync<T>(Func<T> callback, DispatcherPriority priority); |
||||
/// <inheritdoc see="InvokeAsync(Action)"/>
|
||||
Task<T> InvokeAsync<T>(Func<T> callback, DispatcherPriority priority, CancellationToken cancellationToken); |
||||
} |
||||
} |
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Parser |
||||
{ |
||||
/// <summary>
|
||||
/// Interface for global assembly cache service.
|
||||
/// </summary>
|
||||
public interface IGlobalAssemblyCacheService |
||||
{ |
||||
/// <summary>
|
||||
/// Gets whether the file name is within the GAC.
|
||||
/// </summary>
|
||||
bool IsGACAssembly(string fileName); |
||||
|
||||
/// <summary>
|
||||
/// Gets the names of all assemblies in the GAC.
|
||||
/// </summary>
|
||||
IEnumerable<DomAssemblyName> GetGacAssemblyFullNames(); |
||||
|
||||
/// <summary>
|
||||
/// Gets the file name for an assembly stored in the GAC.
|
||||
/// Returns null if the assembly cannot be found.
|
||||
/// </summary>
|
||||
string FindAssemblyInNetGac(DomAssemblyName reference); |
||||
} |
||||
} |
@ -0,0 +1,291 @@
@@ -0,0 +1,291 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
|
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.NRefactory; |
||||
using ICSharpCode.NRefactory.Editor; |
||||
using ICSharpCode.NRefactory.Semantics; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.SharpDevelop.Editor; |
||||
using ICSharpCode.SharpDevelop.Project; |
||||
using ICSharpCode.SharpDevelop.Refactoring; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Parser |
||||
{ |
||||
/// <summary>
|
||||
/// Manages parse runs and caches ParseInformation.
|
||||
/// </summary>
|
||||
public interface IParserService |
||||
{ |
||||
/// <summary>
|
||||
/// Gets/Sets the task list tokens.
|
||||
/// The getter of this property is thread-safe;
|
||||
/// the setter must only be called on the main thread.
|
||||
/// </summary>
|
||||
IReadOnlyList<string> TaskListTokens { get; set; } |
||||
|
||||
#region Load Solution Projects Thread
|
||||
/// <summary>
|
||||
/// Gets whether the solution is being loaded, or a major re-parse is happening
|
||||
/// (e.g. after adding a project).
|
||||
/// </summary>
|
||||
/// <remarks>This property is only changed by the main thread.</remarks>
|
||||
bool LoadSolutionProjectsThreadRunning { get; } |
||||
|
||||
/// <summary>
|
||||
/// This event is raised when the LoadSolutionProjectsThreadRunning property changes to <c>true</c>.
|
||||
/// This always happens on the main thread.
|
||||
/// </summary>
|
||||
event EventHandler LoadSolutionProjectsThreadStarted; |
||||
|
||||
/// <summary>
|
||||
/// This event is raised when the LoadSolutionProjectsThreadRunning property changes to <c>false</c>.
|
||||
/// This always happens on the main thread.
|
||||
/// </summary>
|
||||
event EventHandler LoadSolutionProjectsThreadEnded; // TODO: rename to finished
|
||||
#endregion
|
||||
|
||||
#region GetCompilation
|
||||
/// <summary>
|
||||
/// Gets or creates a compilation for the specified project.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is thread-safe.
|
||||
/// This method never returns null - in case of errors, a dummy compilation is created.
|
||||
/// </remarks>
|
||||
ICompilation GetCompilation(IProject project); |
||||
|
||||
/// <summary>
|
||||
/// Gets or creates a compilation for the project that contains the specified file.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is thread-safe.
|
||||
/// This method never returns null - in case of errors, a dummy compilation is created.
|
||||
/// </remarks>
|
||||
ICompilation GetCompilationForFile(FileName fileName); |
||||
|
||||
/// <summary>
|
||||
/// Gets a snapshot of the current compilations
|
||||
/// This method is useful when a consistent snapshot across multiple compilations is needed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is thread-safe.
|
||||
/// </remarks>
|
||||
SharpDevelopSolutionSnapshot GetCurrentSolutionSnapshot(); |
||||
|
||||
/// <summary>
|
||||
/// Invalidates the current solution snapshot, causing
|
||||
/// the next <see cref="GetCurrentSolutionSnapshot()"/> call to create a new one.
|
||||
/// This method needs to be called whenever IProject.ProjectContent changes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is thread-safe.
|
||||
/// </remarks>
|
||||
void InvalidateCurrentSolutionSnapshot(); |
||||
#endregion
|
||||
|
||||
#region GetExistingParsedFile
|
||||
/// <summary>
|
||||
/// Gets the unresolved type system for the specified file.
|
||||
/// </summary>
|
||||
/// <param name="fileName">Name of the file.</param>
|
||||
/// <param name="version">
|
||||
/// Optional: requested version of the file.
|
||||
/// If this parameter is specified and the existing parsed file belongs to a different version,
|
||||
/// this method will return null.
|
||||
/// </param>
|
||||
/// <param name="parentProject">
|
||||
/// Optional: If the file is part of multiple projects, specifies
|
||||
/// which parsed version of the file to return (for example, different project settings
|
||||
/// can cause the file to be parsed differently).
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Returns the IParsedFile for the specified file,
|
||||
/// or null if the file has not been parsed yet.
|
||||
/// </returns>
|
||||
/// <remarks>This method is thread-safe.</remarks>
|
||||
IParsedFile GetExistingParsedFile(FileName fileName, ITextSourceVersion version = null, IProject parentProject = null); |
||||
|
||||
/// <summary>
|
||||
/// Gets full parse information for the specified file, if it is available.
|
||||
/// </summary>
|
||||
/// <param name="fileName">Name of the file.</param>
|
||||
/// <param name="version">
|
||||
/// Optional: requested version of the file.
|
||||
/// If this parameter is specified and the existing parsed file belongs to a different version,
|
||||
/// this method will return null.
|
||||
/// </param>
|
||||
/// <param name="parentProject">
|
||||
/// Optional: If the file is part of multiple projects, specifies
|
||||
/// which parsed version of the file to return (for example, different project settings
|
||||
/// can cause the file to be parsed differently).
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// If only the IParsedFile is available (non-full parse information), this method returns null.
|
||||
/// </returns>
|
||||
ParseInformation GetCachedParseInformation(FileName fileName, ITextSourceVersion version = null, IProject parentProject = null); |
||||
#endregion
|
||||
|
||||
#region Parse
|
||||
/// <summary>
|
||||
/// Parses the specified file.
|
||||
/// Produces full parse information.
|
||||
/// </summary>
|
||||
/// <param name="fileName">Name of the file to parse</param>
|
||||
/// <param name="fileContent">Optional: Content of the file to parse.</param>
|
||||
/// <param name="parentProject">
|
||||
/// Optional: If the file is part of multiple projects, specifies
|
||||
/// which parsed version of the file to return (for example, different project settings
|
||||
/// can cause the file to be parsed differently).
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Returns the ParseInformation for the specified file, or null if the file cannot be parsed.
|
||||
/// For files currently open in an editor, this method does not necessary reparse, but may return
|
||||
/// an existing cached parse information (but only if it's still up-to-date).
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// This method is thread-safe.
|
||||
/// <para>
|
||||
/// If <paramref name="fileContent"/> is null, this method will block and wait for the main thread
|
||||
/// to retrieve the latest file content. This can cause deadlocks if this method is called within a lock.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If <paramref name="fileContent"/> not null, the exact file version specified will be parsed.
|
||||
/// This method will not wait for the main thread in that case.
|
||||
/// If the specified version is older than the latest version, the old version will be parsed
|
||||
/// and returned, but the old parse information will not be registered.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
ParseInformation Parse(FileName fileName, ITextSource fileContent = null, IProject parentProject = null, |
||||
CancellationToken cancellationToken = default(CancellationToken)); |
||||
|
||||
/// <summary>
|
||||
/// Parses the specified file.
|
||||
/// This method does not request full parse information.
|
||||
/// </summary>
|
||||
/// <param name="fileName">Name of the file to parse</param>
|
||||
/// <param name="fileContent">Optional: Content of the file to parse.</param>
|
||||
/// <param name="parentProject">
|
||||
/// Optional: If the file is part of multiple projects, specifies
|
||||
/// which parsed version of the file to return (for example, different project settings
|
||||
/// can cause the file to be parsed differently).
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Returns the IParsedFile for the specified file, or null if the file cannot be parsed.
|
||||
/// For files currently open in an editor, this method does not necessarily reparse, but may return
|
||||
/// the existing IParsedFile (but only if it's still up-to-date).
|
||||
/// </returns>
|
||||
/// <remarks><inheritdoc cref="Parse"/></remarks>
|
||||
IParsedFile ParseFile(FileName fileName, ITextSource fileContent = null, IProject parentProject = null, |
||||
CancellationToken cancellationToken = default(CancellationToken)); |
||||
|
||||
/// <summary>
|
||||
/// Parses the specified file on a background thread.
|
||||
/// Produces full parse information.
|
||||
/// </summary>
|
||||
/// <param name="fileName">Name of the file to parse</param>
|
||||
/// <param name="fileContent">Optional: Content of the file to parse.</param>
|
||||
/// <param name="parentProject">
|
||||
/// Optional: If the file is part of multiple projects, specifies
|
||||
/// which parsed version of the file to return (for example, different project settings
|
||||
/// can cause the file to be parsed differently).
|
||||
/// </param>
|
||||
/// <returns><inheritdoc cref="Parse"/></returns>
|
||||
/// <remarks>
|
||||
/// This method is thread-safe.
|
||||
/// <para>
|
||||
/// If <paramref name="fileContent"/> is null, the task wait for the main thread
|
||||
/// to retrieve the latest file content.
|
||||
/// This means that waiting for the task can cause deadlocks. (however, using C# 5 <c>await</c> is safe)
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If <paramref name="fileContent"/> not null, the exact file version specified will be parsed.
|
||||
/// This method will not wait for the main thread in that case.
|
||||
/// If the specified version is older than the latest version, the old version will be parsed
|
||||
/// and returned, but the old parse information will not be registered.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
Task<ParseInformation> ParseAsync(FileName fileName, ITextSource fileContent = null, IProject parentProject = null, |
||||
CancellationToken cancellationToken = default(CancellationToken)); |
||||
|
||||
/// <summary>
|
||||
/// Parses the specified file on a background thread.
|
||||
/// This method does not request full parse information.
|
||||
/// </summary>
|
||||
/// <param name="fileName">Name of the file to parse</param>
|
||||
/// <param name="fileContent">Optional: Content of the file to parse.</param>
|
||||
/// <param name="parentProject">
|
||||
/// Optional: If the file is part of multiple projects, specifies
|
||||
/// which parsed version of the file to return (for example, different project settings
|
||||
/// can cause the file to be parsed differently).
|
||||
/// </param>
|
||||
/// <returns><inheritdoc cref="ParseFile"/></returns>
|
||||
/// <remarks><inheritdoc cref="ParseAsync"/></remarks>
|
||||
Task<IParsedFile> ParseFileAsync(FileName fileName, ITextSource fileContent = null, IProject parentProject = null, |
||||
CancellationToken cancellationToken = default(CancellationToken)); |
||||
#endregion
|
||||
|
||||
#region Resolve
|
||||
ResolveResult Resolve(ITextEditor editor, TextLocation location, |
||||
ICompilation compilation = null, |
||||
CancellationToken cancellationToken = default(CancellationToken)); |
||||
|
||||
ResolveResult Resolve(FileName fileName, TextLocation location, |
||||
ITextSource fileContent = null, ICompilation compilation = null, |
||||
CancellationToken cancellationToken = default(CancellationToken)); |
||||
|
||||
Task<ResolveResult> ResolveAsync(FileName fileName, TextLocation location, |
||||
ITextSource fileContent = null, ICompilation compilation = null, |
||||
CancellationToken cancellationToken = default(CancellationToken)); |
||||
|
||||
Task FindLocalReferencesAsync(FileName fileName, IVariable variable, Action<Reference> callback, |
||||
ITextSource fileContent = null, ICompilation compilation = null, |
||||
CancellationToken cancellationToken = default(CancellationToken)); |
||||
#endregion
|
||||
|
||||
#region Parsed File Listeners
|
||||
/// <summary>
|
||||
/// Gets whether a parser is registered for the specified file name.
|
||||
/// </summary>
|
||||
bool HasParser(FileName fileName); |
||||
|
||||
/// <summary>
|
||||
/// Clears the cached parse information.
|
||||
/// If the file does not belong to any project, this also clears the cached type system.
|
||||
/// </summary>
|
||||
void ClearParseInformation(FileName fileName); |
||||
|
||||
/// <summary>
|
||||
/// Adds a project that owns the file and wishes to receive parse information.
|
||||
/// </summary>
|
||||
/// <param name="fileName">Name of the file contained in the project.</param>
|
||||
/// <param name="project">The parent project of the file.</param>
|
||||
/// <param name="startAsyncParse">
|
||||
/// Whether to start an asynchronous parse operation for the specified file.
|
||||
/// </param>
|
||||
/// <param name="isLinkedFile">
|
||||
/// Specified whether the file is linked within the project, i.e. likely also belongs to another project.
|
||||
/// The parser services tries to use the project that contains the file directly (non-linked)
|
||||
/// as the primary parent project.
|
||||
/// </param>
|
||||
void AddOwnerProject(FileName fileName, IProject project, bool startAsyncParse, bool isLinkedFile); |
||||
|
||||
/// <summary>
|
||||
/// Removes a project from the owners of the file.
|
||||
/// This method invokes <c>project.UpdateParseInformation(existingParsedFile, null);</c>.
|
||||
/// (unless existingParsedFile==null)
|
||||
/// </summary>
|
||||
void RemoveOwnerProject(FileName fileName, IProject project); |
||||
|
||||
/// <summary>
|
||||
/// Occurs whenever parse information was updated. This event is raised on the main thread.
|
||||
/// </summary>
|
||||
event EventHandler<ParseInformationEventArgs> ParseInformationUpdated; |
||||
#endregion
|
||||
} |
||||
} |
@ -1,18 +0,0 @@
@@ -1,18 +0,0 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Parser |
||||
{ |
||||
/// <summary>
|
||||
/// Listener for parse info changes.
|
||||
/// Caution: The callback is invoked within the parser service's lock. Beware of deadlocks!
|
||||
/// The method is called on the thread that performed the parse operation, which might be the main thread
|
||||
/// or a background thread.
|
||||
/// If possible, use the <see cref="ParserService.ParseInformationUpdated"/> event instead, which is called on the main thread
|
||||
/// and after the parser service released its lock.
|
||||
/// </summary>
|
||||
public delegate void ParsedFileListener(IParsedFile oldFile, IParsedFile newFile); |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,115 @@
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using ICSharpCode.NRefactory; |
||||
|
||||
namespace ICSharpCode.SharpDevelop |
||||
{ |
||||
/// <summary>
|
||||
/// A dictionary that allows multiple pairs with the same key.
|
||||
/// </summary>
|
||||
public class MultiDictionary<TKey, TValue> : ILookup<TKey, TValue> |
||||
{ |
||||
Dictionary<TKey, List<TValue>> dict; |
||||
|
||||
public MultiDictionary() |
||||
{ |
||||
} |
||||
|
||||
public MultiDictionary(IEqualityComparer<TKey> comparer) |
||||
{ |
||||
dict = new Dictionary<TKey, List<TValue>>(comparer); |
||||
} |
||||
|
||||
public void Add(TKey key, TValue value) |
||||
{ |
||||
List<TValue> valueList; |
||||
if (!dict.TryGetValue(key, out valueList)) { |
||||
valueList = new List<TValue>(); |
||||
dict.Add(key, valueList); |
||||
} |
||||
valueList.Add(value); |
||||
} |
||||
|
||||
public bool Remove(TKey key, TValue value) |
||||
{ |
||||
List<TValue> valueList; |
||||
if (dict.TryGetValue(key, out valueList)) { |
||||
if (valueList.Remove(value)) { |
||||
if (valueList.Count == 0) |
||||
dict.Remove(key); |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public void Clear() |
||||
{ |
||||
dict.Clear(); |
||||
} |
||||
|
||||
public IReadOnlyList<TValue> this[TKey key] { |
||||
get { |
||||
List<TValue> list; |
||||
if (dict.TryGetValue(key, out list)) |
||||
return list; |
||||
else |
||||
return new TValue[0]; |
||||
} |
||||
} |
||||
|
||||
public int Count { |
||||
get { return dict.Count; } |
||||
} |
||||
|
||||
IEnumerable<TValue> ILookup<TKey, TValue>.this[TKey key] { |
||||
get { return this[key]; } |
||||
} |
||||
|
||||
bool ILookup<TKey, TValue>.Contains(TKey key) |
||||
{ |
||||
return dict.ContainsKey(key); |
||||
} |
||||
|
||||
public IEnumerator<IGrouping<TKey, TValue>> GetEnumerator() |
||||
{ |
||||
foreach (var pair in dict) |
||||
yield return new Grouping(pair.Key, pair.Value); |
||||
} |
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() |
||||
{ |
||||
return GetEnumerator(); |
||||
} |
||||
|
||||
sealed class Grouping : IGrouping<TKey, TValue> |
||||
{ |
||||
readonly TKey key; |
||||
readonly List<TValue> values; |
||||
|
||||
public Grouping(TKey key, List<TValue> values) |
||||
{ |
||||
this.key = key; |
||||
this.values = values; |
||||
} |
||||
|
||||
public TKey Key { |
||||
get { return key; } |
||||
} |
||||
|
||||
public IEnumerator<TValue> GetEnumerator() |
||||
{ |
||||
return values.GetEnumerator(); |
||||
} |
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() |
||||
{ |
||||
return values.GetEnumerator(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,112 +0,0 @@
@@ -1,112 +0,0 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Threading; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Util |
||||
{ |
||||
/// <summary>
|
||||
/// A worker thread that normally sleeps, but can run a queue of commands.
|
||||
///
|
||||
/// This class does not create a worker thread on its own, it merely manages tasks for
|
||||
/// the worker thread that calls <see cref="RunLoop"/>.
|
||||
/// </summary>
|
||||
public class WorkerThread |
||||
{ |
||||
sealed class AsyncTask : IAsyncResult |
||||
{ |
||||
internal readonly ManualResetEventSlim manualResetEvent = new ManualResetEventSlim(false); |
||||
internal readonly Action method; |
||||
volatile bool isCompleted; |
||||
|
||||
internal AsyncTask(Action method) |
||||
{ |
||||
this.method = method; |
||||
} |
||||
|
||||
internal void SetCompleted() |
||||
{ |
||||
isCompleted = true; |
||||
manualResetEvent.Set(); |
||||
} |
||||
|
||||
public bool IsCompleted { |
||||
get { return isCompleted; } |
||||
} |
||||
|
||||
public WaitHandle AsyncWaitHandle { |
||||
get { return manualResetEvent.WaitHandle; } |
||||
} |
||||
|
||||
public object AsyncState { get; set; } |
||||
public bool CompletedSynchronously { get { return false; } } |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Runs <paramref name="method"/> on the worker thread.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to run.</param>
|
||||
/// <returns>IAsyncResult that gets completed when the action has executed.</returns>
|
||||
public IAsyncResult Enqueue(Action method) |
||||
{ |
||||
if (method == null) |
||||
throw new ArgumentNullException("method"); |
||||
AsyncTask task = new AsyncTask(method); |
||||
lock (lockObject) { |
||||
taskQueue.Enqueue(task); |
||||
Monitor.Pulse(lockObject); |
||||
} |
||||
return task; |
||||
} |
||||
|
||||
readonly object lockObject = new object(); |
||||
|
||||
// access needs lock using 'lockObject'
|
||||
Queue<AsyncTask> taskQueue = new Queue<AsyncTask>(); |
||||
// access needs lock using 'lockObject'
|
||||
bool workerRunning; |
||||
|
||||
// not a shared variable: accessed only within worker thread
|
||||
bool exitWorker; |
||||
|
||||
/// <summary>
|
||||
/// Runs the worker thread loop on the current thread.
|
||||
/// </summary>
|
||||
public void RunLoop() |
||||
{ |
||||
lock (lockObject) { |
||||
if (workerRunning) |
||||
throw new InvalidOperationException("There already is a worker running"); |
||||
workerRunning = true; |
||||
} |
||||
try { |
||||
exitWorker = false; |
||||
while (!exitWorker) { |
||||
AsyncTask task; |
||||
lock (lockObject) { |
||||
while (taskQueue.Count == 0) |
||||
Monitor.Wait(lockObject); |
||||
task = taskQueue.Dequeue(); |
||||
} |
||||
task.method(); |
||||
task.SetCompleted(); |
||||
} |
||||
} finally { |
||||
lock (lockObject) { |
||||
workerRunning = false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Exits running the worker thread after executing all currently enqueued methods.
|
||||
/// </summary>
|
||||
/// <returns>IAsyncResult that gets completed when the worker thread has shut down.</returns>
|
||||
public IAsyncResult ExitWorkerThread() |
||||
{ |
||||
return Enqueue(delegate { exitWorker = true; }); |
||||
} |
||||
} |
||||
} |
@ -1,120 +0,0 @@
@@ -1,120 +0,0 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.ComponentModel; |
||||
using System.Reflection; |
||||
using System.Threading; |
||||
using System.Windows.Threading; |
||||
|
||||
namespace ICSharpCode.SharpDevelop |
||||
{ |
||||
/// <summary>
|
||||
/// Implements the ISynchronizeInvoke interface by using a WPF dispatcher
|
||||
/// to perform the cross-thread call.
|
||||
/// </summary>
|
||||
sealed class WpfSynchronizeInvoke : ISynchronizeInvoke |
||||
{ |
||||
readonly Dispatcher dispatcher; |
||||
|
||||
public WpfSynchronizeInvoke(Dispatcher dispatcher) |
||||
{ |
||||
if (dispatcher == null) |
||||
throw new ArgumentNullException("dispatcher"); |
||||
this.dispatcher = dispatcher; |
||||
} |
||||
|
||||
public bool InvokeRequired { |
||||
get { |
||||
return !dispatcher.CheckAccess(); |
||||
} |
||||
} |
||||
|
||||
public IAsyncResult BeginInvoke(Delegate method, object[] args) |
||||
{ |
||||
DispatcherOperation op; |
||||
if (args == null || args.Length == 0) |
||||
op = dispatcher.BeginInvoke(DispatcherPriority.Normal, method); |
||||
else if (args.Length == 1) |
||||
op = dispatcher.BeginInvoke(DispatcherPriority.Normal, method, args[0]); |
||||
else |
||||
op = dispatcher.BeginInvoke(DispatcherPriority.Normal, method, args[0], args.Splice(1)); |
||||
return new AsyncResult(op); |
||||
} |
||||
|
||||
sealed class AsyncResult : IAsyncResult |
||||
{ |
||||
internal readonly DispatcherOperation op; |
||||
readonly object lockObj = new object(); |
||||
ManualResetEvent resetEvent; |
||||
|
||||
public AsyncResult(DispatcherOperation op) |
||||
{ |
||||
this.op = op; |
||||
} |
||||
|
||||
public bool IsCompleted { |
||||
get { |
||||
return op.Status == DispatcherOperationStatus.Completed; |
||||
} |
||||
} |
||||
|
||||
public WaitHandle AsyncWaitHandle { |
||||
get { |
||||
lock (lockObj) { |
||||
if (resetEvent == null) { |
||||
op.Completed += op_Completed; |
||||
resetEvent = new ManualResetEvent(false); |
||||
if (IsCompleted) |
||||
resetEvent.Set(); |
||||
} |
||||
return resetEvent; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void op_Completed(object sender, EventArgs e) |
||||
{ |
||||
lock (lockObj) { |
||||
resetEvent.Set(); |
||||
} |
||||
} |
||||
|
||||
public object AsyncState { |
||||
get { return null; } |
||||
} |
||||
|
||||
public bool CompletedSynchronously { |
||||
get { return false; } |
||||
} |
||||
} |
||||
|
||||
public object EndInvoke(IAsyncResult result) |
||||
{ |
||||
AsyncResult r = result as AsyncResult; |
||||
if (r == null) |
||||
throw new ArgumentException("result must be the return value of a WpfSynchronizeInvoke.BeginInvoke call!"); |
||||
r.op.Wait(); |
||||
return r.op.Result; |
||||
} |
||||
|
||||
public object Invoke(Delegate method, object[] args) |
||||
{ |
||||
object result = null; |
||||
Exception exception = null; |
||||
dispatcher.Invoke( |
||||
DispatcherPriority.Normal, |
||||
(Action)delegate { |
||||
try { |
||||
result = method.DynamicInvoke(args); |
||||
} catch (TargetInvocationException ex) { |
||||
exception = ex.InnerException; |
||||
} |
||||
}); |
||||
// if an exception occurred, re-throw it on the calling thread
|
||||
if (exception != null) |
||||
throw new TargetInvocationException(exception); |
||||
return result; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,359 @@
@@ -0,0 +1,359 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Concurrent; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using System.Windows.Threading; |
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.NRefactory; |
||||
using ICSharpCode.NRefactory.Editor; |
||||
using ICSharpCode.NRefactory.Semantics; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
||||
using ICSharpCode.SharpDevelop.Editor; |
||||
using ICSharpCode.SharpDevelop.Gui; |
||||
using ICSharpCode.SharpDevelop.Project; |
||||
using ICSharpCode.SharpDevelop.Refactoring; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Parser |
||||
{ |
||||
sealed class ParserService : IParserService |
||||
{ |
||||
IList<ParserDescriptor> parserDescriptors; |
||||
|
||||
public ParserService() |
||||
{ |
||||
parserDescriptors = AddInTree.BuildItems<ParserDescriptor>("/Workspace/Parser", null, false); |
||||
} |
||||
|
||||
#region ParseInformationUpdated
|
||||
public event EventHandler<ParseInformationEventArgs> ParseInformationUpdated = delegate {}; |
||||
|
||||
internal void RaiseParseInformationUpdated(ParseInformationEventArgs e) |
||||
{ |
||||
// RaiseParseInformationUpdated is called inside a lock, but we don't want to raise the event inside that lock.
|
||||
// To ensure events are raised in the same order, we always invoke on the main thread.
|
||||
WorkbenchSingleton.SafeThreadAsyncCall( |
||||
delegate { |
||||
string addition; |
||||
if (e.OldParsedFile == null) |
||||
addition = " (new)"; |
||||
else if (e.NewParsedFile == null) |
||||
addition = " (removed)"; |
||||
else |
||||
addition = " (updated)"; |
||||
LoggingService.Debug("ParseInformationUpdated " + e.FileName + addition); |
||||
ParseInformationUpdated(null, e); |
||||
}); |
||||
} |
||||
#endregion
|
||||
|
||||
#region TaskListTokens
|
||||
IReadOnlyList<string> taskListTokens = LoadTaskListTokens(); |
||||
|
||||
public IReadOnlyList<string> TaskListTokens { |
||||
get { return taskListTokens; } |
||||
set { |
||||
SD.MainThread.VerifyAccess(); |
||||
if (!value.SequenceEqual(taskListTokens)) { |
||||
taskListTokens = value.ToArray(); |
||||
PropertyService.SetList("SharpDevelop.TaskListTokens", taskListTokens); |
||||
// TODO: trigger reparse?
|
||||
} |
||||
} |
||||
} |
||||
|
||||
static IReadOnlyList<string> LoadTaskListTokens() |
||||
{ |
||||
if (PropertyService.Contains("SharpDevelop.TaskListTokens")) |
||||
return PropertyService.GetList<string>("SharpDevelop.TaskListTokens").ToArray(); |
||||
else |
||||
return new string[] { "HACK", "TODO", "UNDONE", "FIXME" }; |
||||
} |
||||
#endregion
|
||||
|
||||
#region Load Solution Projects Thread
|
||||
public bool LoadSolutionProjectsThreadRunning { |
||||
get { return false; } |
||||
} |
||||
|
||||
public event EventHandler LoadSolutionProjectsThreadStarted; |
||||
public event EventHandler LoadSolutionProjectsThreadEnded; |
||||
#endregion
|
||||
|
||||
#region Compilation
|
||||
public ICompilation GetCompilation(IProject project) |
||||
{ |
||||
return GetCurrentSolutionSnapshot().GetCompilation(project); |
||||
} |
||||
|
||||
public ICompilation GetCompilationForFile(FileName fileName) |
||||
{ |
||||
Solution solution = ProjectService.OpenSolution; |
||||
IProject project = solution != null ? solution.FindProjectContainingFile(fileName) : null; |
||||
if (project != null) |
||||
return GetCompilation(project); |
||||
|
||||
var entry = GetFileEntry(fileName, false); |
||||
if (entry != null && entry.parser != null) { |
||||
var parsedFile = entry.GetExistingParsedFile(null, null); |
||||
if (parsedFile != null) { |
||||
ICompilation compilation = entry.parser.CreateCompilationForSingleFile(fileName, parsedFile); |
||||
if (compilation != null) |
||||
return compilation; |
||||
} |
||||
} |
||||
return MinimalCorlib.Instance.CreateCompilation(); |
||||
} |
||||
|
||||
// Use a WeakReference for caching the solution snapshot - it can require
|
||||
// lots of memory and may not be invalidated soon enough if the user
|
||||
// is only browsing code.
|
||||
volatile WeakReference<SharpDevelopSolutionSnapshot> currentSolutionSnapshot; |
||||
|
||||
public SharpDevelopSolutionSnapshot GetCurrentSolutionSnapshot() |
||||
{ |
||||
var weakRef = currentSolutionSnapshot; |
||||
SharpDevelopSolutionSnapshot result; |
||||
if (weakRef == null || !weakRef.TryGetTarget(out result)) { |
||||
// create new snapshot if we don't have one cached
|
||||
result = new SharpDevelopSolutionSnapshot(ProjectService.OpenSolution); |
||||
currentSolutionSnapshot = new WeakReference<SharpDevelopSolutionSnapshot>(result); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
public void InvalidateCurrentSolutionSnapshot() |
||||
{ |
||||
currentSolutionSnapshot = null; |
||||
} |
||||
#endregion
|
||||
|
||||
#region Entry management
|
||||
const int cachedEntryCount = 5; |
||||
Dictionary<FileName, ParserServiceEntry> fileEntryDict = new Dictionary<FileName, ParserServiceEntry>(); |
||||
Queue<ParserServiceEntry> cacheExpiryQueue = new Queue<ParserServiceEntry>(); |
||||
|
||||
ParserServiceEntry GetFileEntry(FileName fileName, bool createIfMissing) |
||||
{ |
||||
if (fileName == null) |
||||
throw new ArgumentNullException("fileName"); |
||||
ParserServiceEntry entry; |
||||
lock (fileEntryDict) { |
||||
if (!fileEntryDict.TryGetValue(fileName, out entry)) { |
||||
if (!createIfMissing) |
||||
return null; |
||||
entry = new ParserServiceEntry(this, fileName); |
||||
fileEntryDict.Add(fileName, entry); |
||||
} |
||||
} |
||||
return entry; |
||||
} |
||||
|
||||
public void ClearParseInformation(FileName fileName) |
||||
{ |
||||
ParserServiceEntry entry = GetFileEntry(fileName, false); |
||||
if (entry != null) { |
||||
entry.ExpireCache(); |
||||
} |
||||
} |
||||
|
||||
internal void RemoveEntry(ParserServiceEntry entry) |
||||
{ |
||||
Debug.Assert(Monitor.IsEntered(entry)); |
||||
lock (fileEntryDict) { |
||||
ParserServiceEntry entryAtKey; |
||||
if (fileEntryDict.TryGetValue(entry.fileName, out entryAtKey)) { |
||||
if (entry == entryAtKey) |
||||
fileEntryDict.Remove(entry.fileName); |
||||
} |
||||
} |
||||
} |
||||
|
||||
internal void RegisterForCacheExpiry(ParserServiceEntry entry) |
||||
{ |
||||
// This method should not be called within any locks
|
||||
Debug.Assert(!Monitor.IsEntered(entry)); |
||||
ParserServiceEntry expiredItem = null; |
||||
lock (cacheExpiryQueue) { |
||||
if (cacheExpiryQueue.Count >= cachedEntryCount) { |
||||
expiredItem = cacheExpiryQueue.Dequeue(); |
||||
} |
||||
cacheExpiryQueue.Enqueue(entry); |
||||
} |
||||
if (expiredItem != null) |
||||
expiredItem.ExpireCache(); |
||||
} |
||||
|
||||
public void AddOwnerProject(FileName fileName, IProject project, bool startAsyncParse, bool isLinkedFile) |
||||
{ |
||||
if (project == null) |
||||
throw new ArgumentNullException("project"); |
||||
GetFileEntry(fileName, true).AddOwnerProject(project, isLinkedFile); |
||||
} |
||||
|
||||
public void RemoveOwnerProject(FileName fileName, IProject project) |
||||
{ |
||||
if (project == null) |
||||
throw new ArgumentNullException("project"); |
||||
var entry = GetFileEntry(fileName, false); |
||||
if (entry != null) |
||||
entry.RemoveOwnerProject(project); |
||||
} |
||||
#endregion
|
||||
|
||||
#region Forward Parse() calls to entry
|
||||
public IParsedFile GetExistingParsedFile(FileName fileName, ITextSourceVersion version, IProject parentProject) |
||||
{ |
||||
var entry = GetFileEntry(fileName, false); |
||||
if (entry != null) |
||||
return entry.GetExistingParsedFile(version, parentProject); |
||||
else |
||||
return null; |
||||
} |
||||
|
||||
public ParseInformation GetCachedParseInformation(FileName fileName, ITextSourceVersion version, IProject parentProject) |
||||
{ |
||||
var entry = GetFileEntry(fileName, false); |
||||
if (entry != null) |
||||
return entry.GetCachedParseInformation(version, parentProject); |
||||
else |
||||
return null; |
||||
} |
||||
|
||||
public ParseInformation Parse(FileName fileName, ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) |
||||
{ |
||||
return GetFileEntry(fileName, true).Parse(fileContent, parentProject, cancellationToken); |
||||
} |
||||
|
||||
public IParsedFile ParseFile(FileName fileName, ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) |
||||
{ |
||||
return GetFileEntry(fileName, true).ParseFile(fileContent, parentProject, cancellationToken); |
||||
} |
||||
|
||||
public Task<ParseInformation> ParseAsync(FileName fileName, ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) |
||||
{ |
||||
return GetFileEntry(fileName, true).ParseAsync(fileContent, parentProject, cancellationToken); |
||||
} |
||||
|
||||
public Task<IParsedFile> ParseFileAsync(FileName fileName, ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) |
||||
{ |
||||
return GetFileEntry(fileName, true).ParseFileAsync(fileContent, parentProject, cancellationToken); |
||||
} |
||||
#endregion
|
||||
|
||||
#region Resolve
|
||||
public ResolveResult Resolve(ITextEditor editor, TextLocation location, ICompilation compilation, CancellationToken cancellationToken) |
||||
{ |
||||
if (editor == null) |
||||
throw new ArgumentNullException("editor"); |
||||
return Resolve(editor.FileName, location, editor.Document, compilation, cancellationToken); |
||||
} |
||||
|
||||
public ResolveResult Resolve(FileName fileName, TextLocation location, ITextSource fileContent, ICompilation compilation, CancellationToken cancellationToken) |
||||
{ |
||||
var entry = GetFileEntry(fileName, true); |
||||
if (entry.parser == null) |
||||
return ErrorResolveResult.UnknownError; |
||||
IProject project = compilation != null ? compilation.GetProject() : null; |
||||
var parseInfo = entry.Parse(fileContent, project, cancellationToken); |
||||
if (parseInfo == null) |
||||
return ErrorResolveResult.UnknownError; |
||||
if (compilation == null) |
||||
compilation = GetCompilationForFile(fileName); |
||||
ResolveResult rr = entry.parser.Resolve(parseInfo, location, compilation, cancellationToken); |
||||
LoggingService.Debug("Resolved " + location + " to " + rr); |
||||
return rr; |
||||
} |
||||
|
||||
public Task<ResolveResult> ResolveAsync(FileName fileName, TextLocation location, ITextSource fileContent, ICompilation compilation, CancellationToken cancellationToken) |
||||
{ |
||||
var entry = GetFileEntry(fileName, true); |
||||
if (entry.parser == null) |
||||
return Task.FromResult<ResolveResult>(ErrorResolveResult.UnknownError); |
||||
IProject project = compilation != null ? compilation.GetProject() : null; |
||||
return entry.ParseAsync(fileContent, project, cancellationToken).ContinueWith( |
||||
delegate (Task<ParseInformation> parseInfoTask) { |
||||
var parseInfo = parseInfoTask.Result; |
||||
if (parseInfo == null) |
||||
return ErrorResolveResult.UnknownError; |
||||
if (compilation == null) |
||||
compilation = GetCompilationForFile(fileName); |
||||
ResolveResult rr = entry.parser.Resolve(parseInfo, location, compilation, cancellationToken); |
||||
LoggingService.Debug("Resolved " + location + " to " + rr); |
||||
return rr; |
||||
}, cancellationToken); |
||||
} |
||||
|
||||
public async Task FindLocalReferencesAsync(FileName fileName, IVariable variable, Action<Reference> callback, ITextSource fileContent, ICompilation compilation, CancellationToken cancellationToken) |
||||
{ |
||||
var entry = GetFileEntry(fileName, true); |
||||
if (entry.parser == null) |
||||
return; |
||||
if (fileContent == null) |
||||
fileContent = SD.FileService.GetFileContent(fileName); |
||||
if (compilation == null) |
||||
compilation = GetCompilationForFile(fileName); |
||||
var parseInfo = await entry.ParseAsync(fileContent, compilation.GetProject(), cancellationToken).ConfigureAwait(false); |
||||
await Task.Run( |
||||
() => entry.parser.FindLocalReferences(parseInfo, fileContent, variable, compilation, callback, cancellationToken) |
||||
); |
||||
} |
||||
#endregion
|
||||
|
||||
#region HasParser / CreateParser
|
||||
public bool HasParser(FileName fileName) |
||||
{ |
||||
if (fileName == null) |
||||
throw new ArgumentNullException("fileName"); |
||||
if (parserDescriptors == null) |
||||
return false; |
||||
foreach (ParserDescriptor descriptor in parserDescriptors) { |
||||
if (descriptor.CanParse(fileName)) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates a new IParser instance that can parse the specified file.
|
||||
/// This method is thread-safe.
|
||||
/// </summary>
|
||||
internal IParser CreateParser(FileName fileName) |
||||
{ |
||||
if (fileName == null) |
||||
throw new ArgumentNullException("fileName"); |
||||
if (parserDescriptors == null) |
||||
return null; |
||||
foreach (ParserDescriptor descriptor in parserDescriptors) { |
||||
if (descriptor.CanParse(fileName)) { |
||||
IParser p = descriptor.CreateParser(); |
||||
if (p != null) { |
||||
p.TaskListTokens = TaskListTokens; |
||||
return p; |
||||
} |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
#endregion
|
||||
|
||||
internal void StartParserThread() |
||||
{ |
||||
// TODO
|
||||
} |
||||
|
||||
internal void StopParserThread() |
||||
{ |
||||
// TODO
|
||||
} |
||||
} |
||||
} |
@ -0,0 +1,330 @@
@@ -0,0 +1,330 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.IO; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
|
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.NRefactory.Editor; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
||||
using ICSharpCode.SharpDevelop.Project; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Parser |
||||
{ |
||||
sealed class ParserServiceEntry |
||||
{ |
||||
struct ProjectEntry |
||||
{ |
||||
public readonly IProject Project; |
||||
public readonly IParsedFile ParsedFile; |
||||
public readonly ParseInformation CachedParseInformation; |
||||
|
||||
public ProjectEntry(IProject project, IParsedFile parsedFile, ParseInformation cachedParseInformation) |
||||
{ |
||||
this.Project = project; |
||||
this.ParsedFile = parsedFile; |
||||
this.CachedParseInformation = cachedParseInformation; |
||||
} |
||||
} |
||||
|
||||
readonly ParserService parserService; |
||||
internal readonly FileName fileName; |
||||
internal readonly IParser parser; |
||||
List<ProjectEntry> entries = new List<ProjectEntry> { default(ProjectEntry) }; |
||||
ITextSourceVersion currentVersion; |
||||
|
||||
public ParserServiceEntry(ParserService parserService, FileName fileName) |
||||
{ |
||||
this.parserService = parserService; |
||||
this.fileName = fileName; |
||||
this.parser = parserService.CreateParser(fileName); |
||||
} |
||||
|
||||
#region Owner Projects
|
||||
IProject PrimaryProject { |
||||
get { return entries[0].Project; } |
||||
} |
||||
|
||||
int FindIndexForProject(IProject parentProject) |
||||
{ |
||||
if (parentProject == null) |
||||
return 0; |
||||
for (int i = 0; i < entries.Count; i++) { |
||||
if (entries[i].Project == parentProject) |
||||
return i; |
||||
} |
||||
// project not found
|
||||
return -1; |
||||
} |
||||
|
||||
public void AddOwnerProject(IProject project, bool isLinkedFile) |
||||
{ |
||||
Debug.Assert(project != null); |
||||
lock (this) { |
||||
if (FindIndexForProject(project) >= 0) |
||||
throw new InvalidOperationException("The project alreadys owns the file"); |
||||
ProjectEntry newEntry = new ProjectEntry(project, null, null); |
||||
if (entries[0].Project == null) { |
||||
entries[0] = newEntry; |
||||
} else if (isLinkedFile) { |
||||
entries.Add(newEntry); |
||||
} else { |
||||
entries.Insert(0, newEntry); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void RemoveOwnerProject(IProject project) |
||||
{ |
||||
Debug.Assert(project != null); |
||||
lock (this) { |
||||
int index = FindIndexForProject(project); |
||||
if (index < 0) |
||||
throw new InvalidOperationException("The project does not own the file"); |
||||
if (entries.Count == 1) { |
||||
entries[0] = default(ProjectEntry); |
||||
} else { |
||||
entries.RemoveAt(index); |
||||
} |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Compares currentVersion with version.
|
||||
/// -1 = currentVersion is older; 0 = same version; 1 = newVersion is older
|
||||
/// </summary>
|
||||
int CompareVersions(ITextSourceVersion newVersion) |
||||
{ |
||||
if (currentVersion != null && newVersion != null && currentVersion.BelongsToSameDocumentAs(newVersion)) |
||||
return currentVersion.CompareAge(newVersion); |
||||
else |
||||
return -1; |
||||
} |
||||
|
||||
#region Expire Cache + GetExistingParsedFile + GetCachedParseInformation
|
||||
public void ExpireCache() |
||||
{ |
||||
lock (this) { |
||||
if (PrimaryProject == null) { |
||||
parserService.RemoveEntry(this); |
||||
} else { |
||||
for (int i = 0; i < entries.Count; i++) { |
||||
var oldEntry = entries[i]; |
||||
entries[i] = new ProjectEntry(oldEntry.Project, oldEntry.ParsedFile, null); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public IParsedFile GetExistingParsedFile(ITextSourceVersion version, IProject parentProject) |
||||
{ |
||||
lock (this) { |
||||
if (version != null && CompareVersions(version) != 0) { |
||||
return null; |
||||
} |
||||
int index = FindIndexForProject(parentProject); |
||||
if (index < 0) |
||||
return null; |
||||
return entries[index].ParsedFile; |
||||
} |
||||
} |
||||
|
||||
public ParseInformation GetCachedParseInformation(ITextSourceVersion version, IProject parentProject) |
||||
{ |
||||
lock (this) { |
||||
if (version != null && CompareVersions(version) != 0) { |
||||
return null; |
||||
} |
||||
int index = FindIndexForProject(parentProject); |
||||
if (index < 0) |
||||
return null; |
||||
return entries[index].CachedParseInformation; |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
#region Parse
|
||||
public ParseInformation Parse(ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) |
||||
{ |
||||
if (fileContent == null) { |
||||
fileContent = SD.FileService.GetFileContent(fileName); |
||||
} |
||||
|
||||
return DoParse(fileContent, parentProject, false, cancellationToken).CachedParseInformation; |
||||
} |
||||
|
||||
public IParsedFile ParseFile(ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) |
||||
{ |
||||
if (fileContent == null) { |
||||
fileContent = SD.FileService.GetFileContent(fileName); |
||||
} |
||||
|
||||
return DoParse(fileContent, parentProject, false, cancellationToken).ParsedFile; |
||||
} |
||||
|
||||
ProjectEntry DoParse(ITextSource fileContent, IProject parentProject, bool fullParseInformationRequested, |
||||
CancellationToken cancellationToken) |
||||
{ |
||||
if (parser == null) |
||||
return default(ProjectEntry); |
||||
|
||||
if (fileContent == null) { |
||||
// No file content was specified. Because the callers of this method already check for currently open files,
|
||||
// we can assume that the file isn't open and simply read it from disk.
|
||||
try { |
||||
fileContent = SD.FileService.GetFileContentFromDisk(fileName, cancellationToken); |
||||
} catch (IOException) { |
||||
// It is possible that the file gets deleted/becomes inaccessible while a background parse
|
||||
// operation is enqueued, so we have to handle IO exceptions.
|
||||
return default(ProjectEntry); |
||||
} catch (UnauthorizedAccessException) { |
||||
return default(ProjectEntry); |
||||
} |
||||
} |
||||
|
||||
ProjectEntry result; |
||||
lock (this) { |
||||
int index = FindIndexForProject(parentProject); |
||||
int versionComparison = CompareVersions(fileContent.Version); |
||||
if (versionComparison > 0 || index < 0) { |
||||
// We're going backwards in time, or are requesting a project that is not an owner
|
||||
// for this entry.
|
||||
var parseInfo = parser.Parse(fileName, fileContent, fullParseInformationRequested, parentProject, cancellationToken); |
||||
FreezableHelper.Freeze(parseInfo.ParsedFile); |
||||
return new ProjectEntry(parentProject, parseInfo.ParsedFile, parseInfo); |
||||
} else { |
||||
if (versionComparison == 0 && index >= 0) { |
||||
// If full parse info is requested, ensure we have full parse info.
|
||||
if (!(fullParseInformationRequested && entries[index].CachedParseInformation == null)) { |
||||
// We already have the requested version parsed, just return it:
|
||||
return entries[index]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
ParseInformationEventArgs[] results = new ParseInformationEventArgs[entries.Count]; |
||||
for (int i = 0; i < entries.Count; i++) { |
||||
ParseInformation parseInfo; |
||||
try { |
||||
parseInfo = parser.Parse(fileName, fileContent, fullParseInformationRequested, entries[i].Project, cancellationToken); |
||||
} catch (Exception ex) { |
||||
SD.LoggingService.Error("Got " + ex.GetType().Name + " while parsing " + fileName); |
||||
throw; |
||||
} |
||||
if (parseInfo == null) |
||||
throw new NullReferenceException(parser.GetType().Name + ".Parse() returned null"); |
||||
if (fullParseInformationRequested && !parseInfo.IsFullParseInformation) |
||||
throw new InvalidOperationException(parser.GetType().Name + ".Parse() did not return full parse info as requested."); |
||||
FreezableHelper.Freeze(parseInfo.ParsedFile); |
||||
results[i] = new ParseInformationEventArgs(entries[i].Project, entries[i].ParsedFile, parseInfo); |
||||
} |
||||
|
||||
// Only if all parse runs succeeded, register the parse information.
|
||||
currentVersion = fileContent.Version; |
||||
for (int i = 0; i < entries.Count; i++) { |
||||
if (fullParseInformationRequested || entries[i].CachedParseInformation != null) |
||||
entries[i] = new ProjectEntry(entries[i].Project, entries[i].ParsedFile, results[i].NewParseInformation); |
||||
else |
||||
entries[i] = new ProjectEntry(entries[i].Project, entries[i].ParsedFile, null); |
||||
if (entries[i].Project != null) |
||||
entries[i].Project.OnParseInformationUpdated(results[i]); |
||||
parserService.RaiseParseInformationUpdated(results[i]); |
||||
} |
||||
result = entries[index]; |
||||
} // exit lock
|
||||
parserService.RegisterForCacheExpiry(this); |
||||
return result; |
||||
} |
||||
#endregion
|
||||
|
||||
#region ParseAsync
|
||||
Task<ProjectEntry> runningAsyncParseTask; |
||||
ITextSourceVersion runningAsyncParseFileContentVersion; |
||||
bool runningAsyncParseFullInfoRequested; |
||||
|
||||
public async Task<ParseInformation> ParseAsync(ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) |
||||
{ |
||||
return (await DoParseAsync(fileContent, parentProject, true, cancellationToken)).CachedParseInformation; |
||||
} |
||||
|
||||
public async Task<IParsedFile> ParseFileAsync(ITextSource fileContent, IProject parentProject, CancellationToken cancellationToken) |
||||
{ |
||||
return (await DoParseAsync(fileContent, parentProject, false, cancellationToken)).ParsedFile; |
||||
} |
||||
|
||||
Task<ProjectEntry> DoParseAsync(ITextSource fileContent, IProject parentProject, bool requestFullParseInformation, CancellationToken cancellationToken) |
||||
{ |
||||
// Create snapshot of file content, if required
|
||||
bool lookupOpenFileOnTargetThread; |
||||
if (fileContent != null) { |
||||
lookupOpenFileOnTargetThread = false; |
||||
// File content was explicitly specified:
|
||||
// Let's make a snapshot in case the text source is mutable.
|
||||
fileContent = fileContent.CreateSnapshot(); |
||||
} else if (SD.MainThread.InvokeRequired) { |
||||
// fileContent == null && not on the main thread:
|
||||
// Don't fetch the file content right now; if we need to SafeThreadCall() anyways,
|
||||
// it's better to do so from the background task.
|
||||
lookupOpenFileOnTargetThread = true; |
||||
} else { |
||||
// fileContent == null && we are on the main thread:
|
||||
// Let's look up the file in the list of open files right now
|
||||
// so that we don't need to SafeThreadCall() later on.
|
||||
lookupOpenFileOnTargetThread = false; |
||||
fileContent = SD.FileService.GetFileContentForOpenFile(fileName); |
||||
} |
||||
Task<ProjectEntry> task; |
||||
lock (this) { |
||||
if (fileContent != null) { |
||||
// Optimization:
|
||||
// don't start a background task if fileContent was specified and up-to-date parse info is available
|
||||
int index = FindIndexForProject(parentProject); |
||||
int versionComparison = CompareVersions(fileContent.Version); |
||||
if (versionComparison == 0 && index >= 0) { |
||||
// If full parse info is requested, ensure we have full parse info.
|
||||
if (!(requestFullParseInformation && entries[index].CachedParseInformation == null)) { |
||||
// We already have the requested version parsed, just return it:
|
||||
return Task.FromResult(entries[index]); |
||||
} |
||||
} |
||||
// Optimization:
|
||||
// if an equivalent task is already running, return that one instead
|
||||
if (runningAsyncParseTask != null && (!requestFullParseInformation || runningAsyncParseFullInfoRequested) |
||||
&& runningAsyncParseFileContentVersion.BelongsToSameDocumentAs(fileContent.Version) |
||||
&& runningAsyncParseFileContentVersion.CompareAge(fileContent.Version) == 0) |
||||
{ |
||||
return runningAsyncParseTask; |
||||
} |
||||
} |
||||
task = new Task<ProjectEntry>( |
||||
delegate { |
||||
try { |
||||
if (lookupOpenFileOnTargetThread) { |
||||
fileContent = SD.FileService.GetFileContentForOpenFile(fileName); |
||||
} |
||||
return DoParse(fileContent, parentProject, requestFullParseInformation, cancellationToken); |
||||
} finally { |
||||
lock (this) { |
||||
runningAsyncParseTask = null; |
||||
runningAsyncParseFileContentVersion = null; |
||||
} |
||||
} |
||||
}, cancellationToken); |
||||
if (fileContent != null && fileContent.Version != null && !cancellationToken.CanBeCanceled) { |
||||
runningAsyncParseTask = task; |
||||
runningAsyncParseFileContentVersion = fileContent.Version; |
||||
runningAsyncParseFullInfoRequested = requestFullParseInformation; |
||||
} |
||||
} |
||||
task.Start(); |
||||
return task; |
||||
} |
||||
#endregion
|
||||
} |
||||
} |
@ -1,4 +1,4 @@
@@ -1,4 +1,4 @@
|
||||
<Application x:Class="ICSharpCode.SharpDevelop.Gui.App" x:ClassModifier="internal" |
||||
<Application x:Class="ICSharpCode.SharpDevelop.Startup.App" x:ClassModifier="internal" |
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
xmlns:core = "http://icsharpcode.net/sharpdevelop/core" |
@ -0,0 +1,146 @@
@@ -0,0 +1,146 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.ComponentModel; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
using System.Windows.Threading; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Workbench |
||||
{ |
||||
sealed class DispatcherMessageLoop : IMessageLoop, ISynchronizeInvoke |
||||
{ |
||||
readonly Dispatcher dispatcher; |
||||
readonly SynchronizationContext synchronizationContext; |
||||
|
||||
public DispatcherMessageLoop(Dispatcher dispatcher, SynchronizationContext synchronizationContext) |
||||
{ |
||||
this.dispatcher = dispatcher; |
||||
this.synchronizationContext = synchronizationContext; |
||||
} |
||||
|
||||
public Thread Thread { |
||||
get { return dispatcher.Thread; } |
||||
} |
||||
|
||||
public Dispatcher Dispatcher { |
||||
get { return dispatcher; } |
||||
} |
||||
|
||||
public SynchronizationContext SynchronizationContext { |
||||
get { return synchronizationContext; } |
||||
} |
||||
|
||||
public ISynchronizeInvoke SynchronizingObject { |
||||
get { return this; } |
||||
} |
||||
|
||||
public bool InvokeRequired { |
||||
get { return !dispatcher.CheckAccess(); } |
||||
} |
||||
|
||||
public bool CheckAccess() |
||||
{ |
||||
return dispatcher.CheckAccess(); |
||||
} |
||||
|
||||
public void VerifyAccess() |
||||
{ |
||||
dispatcher.VerifyAccess(); |
||||
} |
||||
|
||||
public void InvokeIfRequired(Action callback) |
||||
{ |
||||
if (dispatcher.CheckAccess()) |
||||
callback(); |
||||
else |
||||
dispatcher.Invoke(callback); |
||||
} |
||||
|
||||
public void InvokeIfRequired(Action callback, DispatcherPriority priority) |
||||
{ |
||||
if (dispatcher.CheckAccess()) |
||||
callback(); |
||||
else |
||||
dispatcher.Invoke(callback, priority); |
||||
} |
||||
|
||||
public void InvokeIfRequired(Action callback, DispatcherPriority priority, CancellationToken cancellationToken) |
||||
{ |
||||
if (dispatcher.CheckAccess()) |
||||
callback(); |
||||
else |
||||
dispatcher.Invoke(callback, priority, cancellationToken); |
||||
} |
||||
|
||||
public T InvokeIfRequired<T>(Func<T> callback) |
||||
{ |
||||
if (dispatcher.CheckAccess()) |
||||
return callback(); |
||||
else |
||||
return dispatcher.Invoke(callback); |
||||
} |
||||
|
||||
public T InvokeIfRequired<T>(Func<T> callback, DispatcherPriority priority) |
||||
{ |
||||
if (dispatcher.CheckAccess()) |
||||
return callback(); |
||||
else |
||||
return dispatcher.Invoke(callback, priority); |
||||
} |
||||
|
||||
public T InvokeIfRequired<T>(Func<T> callback, DispatcherPriority priority, CancellationToken cancellationToken) |
||||
{ |
||||
if (dispatcher.CheckAccess()) |
||||
return callback(); |
||||
else |
||||
return dispatcher.Invoke(callback, priority, cancellationToken); |
||||
} |
||||
|
||||
public Task InvokeAsync(Action callback) |
||||
{ |
||||
return dispatcher.InvokeAsync(callback).Task; |
||||
} |
||||
|
||||
public Task InvokeAsync(Action callback, DispatcherPriority priority) |
||||
{ |
||||
return dispatcher.InvokeAsync(callback, priority).Task; |
||||
} |
||||
|
||||
public Task InvokeAsync(Action callback, DispatcherPriority priority, CancellationToken cancellationToken) |
||||
{ |
||||
return dispatcher.InvokeAsync(callback, priority, cancellationToken).Task; |
||||
} |
||||
|
||||
public Task<T> InvokeAsync<T>(Func<T> callback) |
||||
{ |
||||
return dispatcher.InvokeAsync(callback).Task; |
||||
} |
||||
|
||||
public Task<T> InvokeAsync<T>(Func<T> callback, DispatcherPriority priority) |
||||
{ |
||||
return dispatcher.InvokeAsync(callback, priority).Task; |
||||
} |
||||
|
||||
public Task<T> InvokeAsync<T>(Func<T> callback, DispatcherPriority priority, CancellationToken cancellationToken) |
||||
{ |
||||
return dispatcher.InvokeAsync(callback, priority, cancellationToken).Task; |
||||
} |
||||
|
||||
IAsyncResult ISynchronizeInvoke.BeginInvoke(Delegate method, object[] args) |
||||
{ |
||||
return dispatcher.InvokeAsync<object>(() => method.DynamicInvoke(args)).Task; |
||||
} |
||||
|
||||
object ISynchronizeInvoke.EndInvoke(IAsyncResult result) |
||||
{ |
||||
return ((Task<object>)result).Result; |
||||
} |
||||
|
||||
object ISynchronizeInvoke.Invoke(Delegate method, object[] args) |
||||
{ |
||||
return dispatcher.Invoke(() => method.DynamicInvoke(args)); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.IO; |
||||
using System.Text; |
||||
using System.Threading; |
||||
using ICSharpCode.AvalonEdit.Utils; |
||||
using ICSharpCode.Core; |
||||
using ICSharpCode.NRefactory.Editor; |
||||
using ICSharpCode.SharpDevelop.Editor; |
||||
|
||||
namespace ICSharpCode.SharpDevelop.Workbench |
||||
{ |
||||
sealed class FileService : IFileService |
||||
{ |
||||
public Encoding DefaultFileEncoding { |
||||
get { |
||||
return Encoding.GetEncoding(SharpDevelop.FileService.DefaultFileEncodingCodePage); |
||||
} |
||||
} |
||||
|
||||
public ITextSource GetFileContent(FileName fileName) |
||||
{ |
||||
return GetFileContentForOpenFile(fileName) ?? GetFileContentFromDisk(fileName, CancellationToken.None); |
||||
} |
||||
|
||||
public ITextSource GetFileContent(string fileName) |
||||
{ |
||||
return GetFileContent(FileName.Create(fileName)); |
||||
} |
||||
|
||||
public ITextSource GetFileContentForOpenFile(FileName fileName) |
||||
{ |
||||
return SD.MainThread.InvokeIfRequired( |
||||
delegate { |
||||
OpenedFile file = SharpDevelop.FileService.GetOpenedFile(fileName); |
||||
if (file != null) { |
||||
IFileDocumentProvider p = file.CurrentView as IFileDocumentProvider; |
||||
if (p != null) { |
||||
IDocument document = p.GetDocumentForFile(file); |
||||
if (document != null) { |
||||
return document.CreateSnapshot(); |
||||
} |
||||
} |
||||
|
||||
using (Stream s = file.OpenRead()) { |
||||
// load file
|
||||
return new StringTextSource(FileReader.ReadFileContent(s, DefaultFileEncoding)); |
||||
} |
||||
} |
||||
return null; |
||||
}); |
||||
} |
||||
|
||||
public ITextSource GetFileContentFromDisk(FileName fileName, CancellationToken cancellationToken) |
||||
{ |
||||
return new StringTextSource(FileReader.ReadFileContent(fileName, DefaultFileEncoding)); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue