Fixing project names to fix VS bugs

For whatever reason, Visual Studio throws a fit if a referenced project has a name
and the folder name (which is used to reference the project) is different than that name.
To solve this issue, I've renamed all the projects and folders to match their project
names as stated in the project.json.
This commit is contained in:
Benjamin Russell
2016-07-29 16:55:44 -07:00
parent bb0cd461b6
commit e83d2704b9
83 changed files with 5 additions and 5 deletions

View File

@@ -0,0 +1,52 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.SqlTools.EditorServices.Utility
{
/// <summary>
/// Simplifies the setup of a SynchronizationContext for the use
/// of async calls in the current thread.
/// </summary>
public static class AsyncContext
{
/// <summary>
/// Starts a new ThreadSynchronizationContext, attaches it to
/// the thread, and then runs the given async main function.
/// </summary>
/// <param name="asyncMainFunc">
/// The Task-returning Func which represents the "main" function
/// for the thread.
/// </param>
public static void Start(Func<Task> asyncMainFunc)
{
// Is there already a synchronization context?
if (SynchronizationContext.Current != null)
{
throw new InvalidOperationException(
"A SynchronizationContext is already assigned on this thread.");
}
// Create and register a synchronization context for this thread
var threadSyncContext = new ThreadSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(threadSyncContext);
// Get the main task and act on its completion
Task asyncMainTask = asyncMainFunc();
asyncMainTask.ContinueWith(
t => threadSyncContext.EndLoop(),
TaskScheduler.Default);
// Start the synchronization context's request loop and
// wait for the main task to complete
threadSyncContext.RunLoopOnCurrentThread();
asyncMainTask.GetAwaiter().GetResult();
}
}
}

View File

@@ -0,0 +1,85 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.SqlTools.EditorServices.Utility
{
/// <summary>
/// Provides a simplified interface for creating a new thread
/// and establishing an AsyncContext in it.
/// </summary>
public class AsyncContextThread
{
#region Private Fields
private Task threadTask;
private string threadName;
private CancellationTokenSource threadCancellationToken =
new CancellationTokenSource();
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the AsyncContextThread class.
/// </summary>
/// <param name="threadName">
/// The name of the thread for debugging purposes.
/// </param>
public AsyncContextThread(string threadName)
{
this.threadName = threadName;
}
#endregion
#region Public Methods
/// <summary>
/// Runs a task on the AsyncContextThread.
/// </summary>
/// <param name="taskReturningFunc">
/// A Func which returns the task to be run on the thread.
/// </param>
/// <returns>
/// A Task which can be used to monitor the thread for completion.
/// </returns>
public Task Run(Func<Task> taskReturningFunc)
{
// Start up a long-running task with the action as the
// main entry point for the thread
this.threadTask =
Task.Factory.StartNew(
() =>
{
// Set the thread's name to help with debugging
Thread.CurrentThread.Name = "AsyncContextThread: " + this.threadName;
// Set up an AsyncContext to run the task
AsyncContext.Start(taskReturningFunc);
},
this.threadCancellationToken.Token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
return this.threadTask;
}
/// <summary>
/// Stops the thread task.
/// </summary>
public void Stop()
{
this.threadCancellationToken.Cancel();
}
#endregion
}
}

View File

@@ -0,0 +1,103 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.SqlTools.EditorServices.Utility
{
/// <summary>
/// Provides a simple wrapper over a SemaphoreSlim to allow
/// synchronization locking inside of async calls. Cannot be
/// used recursively.
/// </summary>
public class AsyncLock
{
#region Fields
private Task<IDisposable> lockReleaseTask;
private SemaphoreSlim lockSemaphore = new SemaphoreSlim(1, 1);
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the AsyncLock class.
/// </summary>
public AsyncLock()
{
this.lockReleaseTask =
Task.FromResult(
(IDisposable)new LockReleaser(this));
}
#endregion
#region Public Methods
/// <summary>
/// Locks
/// </summary>
/// <returns>A task which has an IDisposable</returns>
public Task<IDisposable> LockAsync()
{
return this.LockAsync(CancellationToken.None);
}
/// <summary>
/// Obtains or waits for a lock which can be used to synchronize
/// access to a resource. The wait may be cancelled with the
/// given CancellationToken.
/// </summary>
/// <param name="cancellationToken">
/// A CancellationToken which can be used to cancel the lock.
/// </param>
/// <returns></returns>
public Task<IDisposable> LockAsync(CancellationToken cancellationToken)
{
Task waitTask = lockSemaphore.WaitAsync(cancellationToken);
return waitTask.IsCompleted ?
this.lockReleaseTask :
waitTask.ContinueWith(
(t, releaser) =>
{
return (IDisposable)releaser;
},
this.lockReleaseTask.Result,
cancellationToken,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}
#endregion
#region Private Classes
/// <summary>
/// Provides an IDisposable wrapper around an AsyncLock so
/// that it can easily be used inside of a 'using' block.
/// </summary>
private class LockReleaser : IDisposable
{
private AsyncLock lockToRelease;
internal LockReleaser(AsyncLock lockToRelease)
{
this.lockToRelease = lockToRelease;
}
public void Dispose()
{
this.lockToRelease.lockSemaphore.Release();
}
}
#endregion
}
}

View File

@@ -0,0 +1,155 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.SqlTools.EditorServices.Utility
{
/// <summary>
/// Provides a synchronized queue which can be used from within async
/// operations. This is primarily used for producer/consumer scenarios.
/// </summary>
/// <typeparam name="T">The type of item contained in the queue.</typeparam>
public class AsyncQueue<T>
{
#region Private Fields
private AsyncLock queueLock = new AsyncLock();
private Queue<T> itemQueue;
private Queue<TaskCompletionSource<T>> requestQueue;
#endregion
#region Properties
/// <summary>
/// Returns true if the queue is currently empty.
/// </summary>
public bool IsEmpty { get; private set; }
#endregion
#region Constructors
/// <summary>
/// Initializes an empty instance of the AsyncQueue class.
/// </summary>
public AsyncQueue() : this(Enumerable.Empty<T>())
{
}
/// <summary>
/// Initializes an instance of the AsyncQueue class, pre-populated
/// with the given collection of items.
/// </summary>
/// <param name="initialItems">
/// An IEnumerable containing the initial items with which the queue will
/// be populated.
/// </param>
public AsyncQueue(IEnumerable<T> initialItems)
{
this.itemQueue = new Queue<T>(initialItems);
this.requestQueue = new Queue<TaskCompletionSource<T>>();
}
#endregion
#region Public Methods
/// <summary>
/// Enqueues an item onto the end of the queue.
/// </summary>
/// <param name="item">The item to be added to the queue.</param>
/// <returns>
/// A Task which can be awaited until the synchronized enqueue
/// operation completes.
/// </returns>
public async Task EnqueueAsync(T item)
{
using (await queueLock.LockAsync())
{
TaskCompletionSource<T> requestTaskSource = null;
// Are any requests waiting?
while (this.requestQueue.Count > 0)
{
// Is the next request cancelled already?
requestTaskSource = this.requestQueue.Dequeue();
if (!requestTaskSource.Task.IsCanceled)
{
// Dispatch the item
requestTaskSource.SetResult(item);
return;
}
}
// No more requests waiting, queue the item for a later request
this.itemQueue.Enqueue(item);
this.IsEmpty = false;
}
}
/// <summary>
/// Dequeues an item from the queue or waits asynchronously
/// until an item is available.
/// </summary>
/// <returns>
/// A Task which can be awaited until a value can be dequeued.
/// </returns>
public Task<T> DequeueAsync()
{
return this.DequeueAsync(CancellationToken.None);
}
/// <summary>
/// Dequeues an item from the queue or waits asynchronously
/// until an item is available. The wait can be cancelled
/// using the given CancellationToken.
/// </summary>
/// <param name="cancellationToken">
/// A CancellationToken with which a dequeue wait can be cancelled.
/// </param>
/// <returns>
/// A Task which can be awaited until a value can be dequeued.
/// </returns>
public async Task<T> DequeueAsync(CancellationToken cancellationToken)
{
Task<T> requestTask;
using (await queueLock.LockAsync(cancellationToken))
{
if (this.itemQueue.Count > 0)
{
// Items are waiting to be taken so take one immediately
T item = this.itemQueue.Dequeue();
this.IsEmpty = this.itemQueue.Count == 0;
return item;
}
else
{
// Queue the request for the next item
var requestTaskSource = new TaskCompletionSource<T>();
this.requestQueue.Enqueue(requestTaskSource);
// Register the wait task for cancel notifications
cancellationToken.Register(
() => requestTaskSource.TrySetCanceled());
requestTask = requestTaskSource.Task;
}
}
// Wait for the request task to complete outside of the lock
return await requestTask;
}
#endregion
}
}

View File

@@ -0,0 +1,34 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.EditorServices.Utility
{
internal static class ObjectExtensions
{
/// <summary>
/// Extension to evaluate an object's ToString() method in an exception safe way. This will
/// extension method will not throw.
/// </summary>
/// <param name="obj">The object on which to call ToString()</param>
/// <returns>The ToString() return value or a suitable error message is that throws.</returns>
public static string SafeToString(this object obj)
{
string str;
try
{
str = obj.ToString();
}
catch (Exception ex)
{
str = $"<Error converting poperty value to string - {ex.Message}>";
}
return str;
}
}
}

View File

@@ -0,0 +1,222 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
namespace Microsoft.SqlTools.EditorServices.Utility
{
/// <summary>
/// Defines the level indicators for log messages.
/// </summary>
public enum LogLevel
{
/// <summary>
/// Indicates a verbose log message.
/// </summary>
Verbose,
/// <summary>
/// Indicates a normal, non-verbose log message.
/// </summary>
Normal,
/// <summary>
/// Indicates a warning message.
/// </summary>
Warning,
/// <summary>
/// Indicates an error message.
/// </summary>
Error
}
/// <summary>
/// Provides a simple logging interface. May be replaced with a
/// more robust solution at a later date.
/// </summary>
public static class Logger
{
private static LogWriter logWriter;
/// <summary>
/// Initializes the Logger for the current session.
/// </summary>
/// <param name="logFilePath">
/// Optional. Specifies the path at which log messages will be written.
/// </param>
/// <param name="minimumLogLevel">
/// Optional. Specifies the minimum log message level to write to the log file.
/// </param>
public static void Initialize(
string logFilePath = "SqlToolsService.log",
LogLevel minimumLogLevel = LogLevel.Normal)
{
if (logWriter != null)
{
logWriter.Dispose();
}
// TODO: Parameterize this
logWriter =
new LogWriter(
minimumLogLevel,
logFilePath,
true);
}
/// <summary>
/// Closes the Logger.
/// </summary>
public static void Close()
{
if (logWriter != null)
{
logWriter.Dispose();
}
}
/// <summary>
/// Writes a message to the log file.
/// </summary>
/// <param name="logLevel">The level at which the message will be written.</param>
/// <param name="logMessage">The message text to be written.</param>
/// <param name="callerName">The name of the calling method.</param>
/// <param name="callerSourceFile">The source file path where the calling method exists.</param>
/// <param name="callerLineNumber">The line number of the calling method.</param>
public static void Write(
LogLevel logLevel,
string logMessage,
[CallerMemberName] string callerName = null,
[CallerFilePath] string callerSourceFile = null,
[CallerLineNumber] int callerLineNumber = 0)
{
if (logWriter != null)
{
logWriter.Write(
logLevel,
logMessage,
callerName,
callerSourceFile,
callerLineNumber);
}
}
}
internal class LogWriter : IDisposable
{
private TextWriter textWriter;
private LogLevel minimumLogLevel = LogLevel.Verbose;
public LogWriter(LogLevel minimumLogLevel, string logFilePath, bool deleteExisting)
{
this.minimumLogLevel = minimumLogLevel;
// Ensure that we have a usable log file path
if (!Path.IsPathRooted(logFilePath))
{
logFilePath =
Path.Combine(
AppContext.BaseDirectory,
logFilePath);
}
if (!this.TryOpenLogFile(logFilePath, deleteExisting))
{
// If the log file couldn't be opened at this location,
// try opening it in a more reliable path
this.TryOpenLogFile(
Path.Combine(
Environment.GetEnvironmentVariable("TEMP"),
Path.GetFileName(logFilePath)),
deleteExisting);
}
}
public void Write(
LogLevel logLevel,
string logMessage,
string callerName = null,
string callerSourceFile = null,
int callerLineNumber = 0)
{
if (this.textWriter != null &&
logLevel >= this.minimumLogLevel)
{
// Print the timestamp and log level
this.textWriter.WriteLine(
"{0} [{1}] - Method \"{2}\" at line {3} of {4}\r\n",
DateTime.Now,
logLevel.ToString().ToUpper(),
callerName,
callerLineNumber,
callerSourceFile);
// Print out indented message lines
foreach (var messageLine in logMessage.Split('\n'))
{
this.textWriter.WriteLine(" " + messageLine.TrimEnd());
}
// Finish with a newline and flush the writer
this.textWriter.WriteLine();
this.textWriter.Flush();
}
}
public void Dispose()
{
if (this.textWriter != null)
{
this.textWriter.Flush();
this.textWriter.Dispose();
this.textWriter = null;
}
}
private bool TryOpenLogFile(
string logFilePath,
bool deleteExisting)
{
try
{
// Make sure the log directory exists
Directory.CreateDirectory(
Path.GetDirectoryName(
logFilePath));
// Open the log file for writing with UTF8 encoding
this.textWriter =
new StreamWriter(
new FileStream(
logFilePath,
deleteExisting ?
FileMode.Create :
FileMode.Append),
Encoding.UTF8);
return true;
}
catch (Exception e)
{
if (e is UnauthorizedAccessException ||
e is IOException)
{
// This exception is thrown when we can't open the file
// at the path in logFilePath. Return false to indicate
// that the log file couldn't be created.
return false;
}
// Unexpected exception, rethrow it
throw;
}
}
}
}

View File

@@ -0,0 +1,77 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Concurrent;
using System.Threading;
namespace Microsoft.SqlTools.EditorServices.Utility
{
/// <summary>
/// Provides a SynchronizationContext implementation that can be used
/// in console applications or any thread which doesn't have its
/// own SynchronizationContext.
/// </summary>
public class ThreadSynchronizationContext : SynchronizationContext
{
#region Private Fields
private BlockingCollection<Tuple<SendOrPostCallback, object>> requestQueue =
new BlockingCollection<Tuple<SendOrPostCallback, object>>();
#endregion
#region Constructors
/// <summary>
/// Posts a request for execution to the SynchronizationContext.
/// This will be executed on the SynchronizationContext's thread.
/// </summary>
/// <param name="callback">
/// The callback to be invoked on the SynchronizationContext's thread.
/// </param>
/// <param name="state">
/// A state object to pass along to the callback when executed through
/// the SynchronizationContext.
/// </param>
public override void Post(SendOrPostCallback callback, object state)
{
// Add the request to the queue
this.requestQueue.Add(
new Tuple<SendOrPostCallback, object>(
callback, state));
}
#endregion
#region Public Methods
/// <summary>
/// Starts the SynchronizationContext message loop on the current thread.
/// </summary>
public void RunLoopOnCurrentThread()
{
Tuple<SendOrPostCallback, object> request;
while (this.requestQueue.TryTake(out request, Timeout.Infinite))
{
// Invoke the request's callback
request.Item1(request.Item2);
}
}
/// <summary>
/// Ends the SynchronizationContext message loop.
/// </summary>
public void EndLoop()
{
// Tell the blocking queue that we're done
this.requestQueue.CompleteAdding();
}
#endregion
}
}

View File

@@ -0,0 +1,143 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
namespace Microsoft.SqlTools.EditorServices.Utility
{
/// <summary>
/// Provides common validation methods to simplify method
/// parameter checks.
/// </summary>
public static class Validate
{
/// <summary>
/// Throws ArgumentNullException if value is null.
/// </summary>
/// <param name="parameterName">The name of the parameter being validated.</param>
/// <param name="valueToCheck">The value of the parameter being validated.</param>
public static void IsNotNull(string parameterName, object valueToCheck)
{
if (valueToCheck == null)
{
throw new ArgumentNullException(parameterName);
}
}
/// <summary>
/// Throws ArgumentOutOfRangeException if the value is outside
/// of the given lower and upper limits.
/// </summary>
/// <param name="parameterName">The name of the parameter being validated.</param>
/// <param name="valueToCheck">The value of the parameter being validated.</param>
/// <param name="lowerLimit">The lower limit which the value should not be less than.</param>
/// <param name="upperLimit">The upper limit which the value should not be greater than.</param>
public static void IsWithinRange(
string parameterName,
int valueToCheck,
int lowerLimit,
int upperLimit)
{
// TODO: Debug assert here if lowerLimit >= upperLimit
if (valueToCheck < lowerLimit || valueToCheck > upperLimit)
{
throw new ArgumentOutOfRangeException(
parameterName,
valueToCheck,
string.Format(
"Value is not between {0} and {1}",
lowerLimit,
upperLimit));
}
}
/// <summary>
/// Throws ArgumentOutOfRangeException if the value is greater than or equal
/// to the given upper limit.
/// </summary>
/// <param name="parameterName">The name of the parameter being validated.</param>
/// <param name="valueToCheck">The value of the parameter being validated.</param>
/// <param name="upperLimit">The upper limit which the value should be less than.</param>
public static void IsLessThan(
string parameterName,
int valueToCheck,
int upperLimit)
{
if (valueToCheck >= upperLimit)
{
throw new ArgumentOutOfRangeException(
parameterName,
valueToCheck,
string.Format(
"Value is greater than or equal to {0}",
upperLimit));
}
}
/// <summary>
/// Throws ArgumentOutOfRangeException if the value is less than or equal
/// to the given lower limit.
/// </summary>
/// <param name="parameterName">The name of the parameter being validated.</param>
/// <param name="valueToCheck">The value of the parameter being validated.</param>
/// <param name="lowerLimit">The lower limit which the value should be greater than.</param>
public static void IsGreaterThan(
string parameterName,
int valueToCheck,
int lowerLimit)
{
if (valueToCheck < lowerLimit)
{
throw new ArgumentOutOfRangeException(
parameterName,
valueToCheck,
string.Format(
"Value is less than or equal to {0}",
lowerLimit));
}
}
/// <summary>
/// Throws ArgumentException if the value is equal to the undesired value.
/// </summary>
/// <typeparam name="TValue">The type of value to be validated.</typeparam>
/// <param name="parameterName">The name of the parameter being validated.</param>
/// <param name="undesiredValue">The value that valueToCheck should not equal.</param>
/// <param name="valueToCheck">The value of the parameter being validated.</param>
public static void IsNotEqual<TValue>(
string parameterName,
TValue valueToCheck,
TValue undesiredValue)
{
if (EqualityComparer<TValue>.Default.Equals(valueToCheck, undesiredValue))
{
throw new ArgumentException(
string.Format(
"The given value '{0}' should not equal '{1}'",
valueToCheck,
undesiredValue),
parameterName);
}
}
/// <summary>
/// Throws ArgumentException if the value is null, an empty string,
/// or a string containing only whitespace.
/// </summary>
/// <param name="parameterName">The name of the parameter being validated.</param>
/// <param name="valueToCheck">The value of the parameter being validated.</param>
public static void IsNotNullOrEmptyString(string parameterName, string valueToCheck)
{
if (string.IsNullOrWhiteSpace(valueToCheck))
{
throw new ArgumentException(
"Parameter contains a null, empty, or whitespace string.",
parameterName);
}
}
}
}