mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
Merge pull request #8 from Microsoft/feature/componentizeServiceHost
Feature/componentize service host
This commit is contained in:
@@ -3,9 +3,9 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Connection
|
namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Message format for the initial connection request
|
/// Message format for the initial connection request
|
||||||
|
|||||||
@@ -7,20 +7,47 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Data.SqlClient;
|
using System.Data.SqlClient;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.EditorServices.Utility;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Connection
|
namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main class for the Connection Management services
|
/// Main class for the Connection Management services
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ConnectionService
|
public class ConnectionService
|
||||||
{
|
{
|
||||||
|
#region Singleton Instance Implementation
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Singleton service instance
|
/// Singleton service instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static Lazy<ConnectionService> instance
|
private static Lazy<ConnectionService> instance
|
||||||
= new Lazy<ConnectionService>(() => new ConnectionService());
|
= new Lazy<ConnectionService>(() => new ConnectionService());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the singleton service instance
|
||||||
|
/// </summary>
|
||||||
|
public static ConnectionService Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return instance.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor is private since it's a singleton class
|
||||||
|
/// </summary>
|
||||||
|
private ConnectionService()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The SQL connection factory object
|
/// The SQL connection factory object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -60,17 +87,6 @@ namespace Microsoft.SqlTools.EditorServices.Connection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the singleton service instance
|
|
||||||
/// </summary>
|
|
||||||
public static ConnectionService Instance
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return instance.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the SQL connection factory instance
|
/// Gets the SQL connection factory instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -86,12 +102,7 @@ namespace Microsoft.SqlTools.EditorServices.Connection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
#endregion
|
||||||
/// Default constructor is private since it's a singleton class
|
|
||||||
/// </summary>
|
|
||||||
private ConnectionService()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Test constructor that injects dependency interfaces
|
/// Test constructor that injects dependency interfaces
|
||||||
@@ -102,6 +113,8 @@ namespace Microsoft.SqlTools.EditorServices.Connection
|
|||||||
this.connectionFactory = testFactory;
|
this.connectionFactory = testFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Open a connection with the specified connection details
|
/// Open a connection with the specified connection details
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -133,6 +146,12 @@ namespace Microsoft.SqlTools.EditorServices.Connection
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Initialize(ServiceHost serviceHost)
|
||||||
|
{
|
||||||
|
// Register request and event handlers with the Service Host
|
||||||
|
serviceHost.SetRequestHandler(ConnectionRequest.Type, HandleConnectRequest);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a new method to be called when the onconnection request is submitted
|
/// Add a new method to be called when the onconnection request is submitted
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -140,7 +159,33 @@ namespace Microsoft.SqlTools.EditorServices.Connection
|
|||||||
public void RegisterOnConnectionTask(OnConnectionHandler activity)
|
public void RegisterOnConnectionTask(OnConnectionHandler activity)
|
||||||
{
|
{
|
||||||
onConnectionActivities.Add(activity);
|
onConnectionActivities.Add(activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Request Handlers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle new connection requests
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connectionDetails"></param>
|
||||||
|
/// <param name="requestContext"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected async Task HandleConnectRequest(
|
||||||
|
ConnectionDetails connectionDetails,
|
||||||
|
RequestContext<ConnectionResult> requestContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleConnectRequest");
|
||||||
|
|
||||||
|
// open connection base on request details
|
||||||
|
ConnectionResult result = ConnectionService.Instance.Connect(connectionDetails);
|
||||||
|
|
||||||
|
await requestContext.SendResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Helpers
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Build a connection string from a connection details instance
|
/// Build a connection string from a connection details instance
|
||||||
@@ -156,5 +201,7 @@ namespace Microsoft.SqlTools.EditorServices.Connection
|
|||||||
connectionBuilder["Initial Catalog"] = connectionDetails.DatabaseName;
|
connectionBuilder["Initial Catalog"] = connectionDetails.DatabaseName;
|
||||||
return connectionBuilder.ToString();
|
return connectionBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Connection
|
namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interface for the SQL Connection factory
|
/// Interface for the SQL Connection factory
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using System.Collections.Generic;
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Data.SqlClient;
|
using System.Data.SqlClient;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Connection
|
namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Factory class to create SqlClientConnections
|
/// Factory class to create SqlClientConnections
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a class that describes the capabilities of a language
|
/// Defines a class that describes the capabilities of a language
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Contracts
|
||||||
{
|
{
|
||||||
public class InitializeRequest
|
public class InitializeRequest
|
||||||
{
|
{
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Contracts
|
||||||
{
|
{
|
||||||
public class ServerCapabilities
|
public class ServerCapabilities
|
||||||
{
|
{
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a message that is sent from the client to request
|
/// Defines a message that is sent from the client to request
|
||||||
@@ -3,10 +3,10 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Serializers;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Channel
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a base implementation for servers and their clients over a
|
/// Defines a base implementation for servers and their clients over a
|
||||||
@@ -7,8 +7,9 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Channel
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides a client implementation for the standard I/O channel.
|
/// Provides a client implementation for the standard I/O channel.
|
||||||
@@ -6,8 +6,9 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Channel
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides a server implementation for the standard I/O channel.
|
/// Provides a server implementation for the standard I/O channel.
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||||
{
|
{
|
||||||
public static class Constants
|
public static class Constants
|
||||||
{
|
{
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines an event type with a particular method name.
|
/// Defines an event type with a particular method name.
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines all possible message types.
|
/// Defines all possible message types.
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("RequestType MethodName = {MethodName}")]
|
[DebuggerDisplay("RequestType MethodName = {MethodName}")]
|
||||||
public class RequestType<TParams, TResult>
|
public class RequestType<TParams, TResult>
|
||||||
@@ -4,8 +4,9 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides context for a received event so that handlers
|
/// Provides context for a received event so that handlers
|
||||||
@@ -4,8 +4,9 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||||
{
|
{
|
||||||
internal interface IMessageSender
|
internal interface IMessageSender
|
||||||
{
|
{
|
||||||
@@ -3,15 +3,16 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Channel;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Utility;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.EditorServices.Utility;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||||
{
|
{
|
||||||
public class MessageDispatcher
|
public class MessageDispatcher
|
||||||
{
|
{
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||||
{
|
{
|
||||||
public class MessageParseException : Exception
|
public class MessageParseException : Exception
|
||||||
{
|
{
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines the possible message protocol types.
|
/// Defines the possible message protocol types.
|
||||||
@@ -3,16 +3,18 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Utility;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.EditorServices.Utility;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||||
{
|
{
|
||||||
public class MessageReader
|
public class MessageReader
|
||||||
{
|
{
|
||||||
@@ -3,14 +3,16 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Utility;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.EditorServices.Utility;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||||
{
|
{
|
||||||
public class MessageWriter
|
public class MessageWriter
|
||||||
{
|
{
|
||||||
@@ -3,13 +3,14 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Channel;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides behavior for a client or server endpoint that
|
/// Provides behavior for a client or server endpoint that
|
||||||
@@ -3,10 +3,11 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol
|
||||||
{
|
{
|
||||||
public class RequestContext<TResult>
|
public class RequestContext<TResult>
|
||||||
{
|
{
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a common interface for message serializers.
|
/// Defines a common interface for message serializers.
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Serializers
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serializes messages in the JSON RPC format. Used primarily
|
/// Serializes messages in the JSON RPC format. Used primarily
|
||||||
@@ -5,8 +5,9 @@
|
|||||||
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System;
|
using System;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Serializers
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serializes messages in the V8 format. Used primarily for debug adapters.
|
/// Serializes messages in the V8 format. Used primarily for debug adapters.
|
||||||
148
src/ServiceHost/Hosting/ServiceHost.cs
Normal file
148
src/ServiceHost/Hosting/ServiceHost.cs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
//
|
||||||
|
// 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.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.SqlTools.EditorServices.Utility;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// SQL Tools VS Code Language Server request handler. Provides the entire JSON RPC
|
||||||
|
/// implementation for sending/receiving JSON requests and dispatching the requests to
|
||||||
|
/// handlers that are registered prior to startup.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ServiceHost : ServiceHostBase
|
||||||
|
{
|
||||||
|
#region Singleton Instance Code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Singleton instance of the service host for internal storage
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Lazy<ServiceHost> instance = new Lazy<ServiceHost>(() => new ServiceHost());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current instance of the ServiceHost
|
||||||
|
/// </summary>
|
||||||
|
public static ServiceHost Instance
|
||||||
|
{
|
||||||
|
get { return instance.Value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs new instance of ServiceHost using the host and profile details provided.
|
||||||
|
/// Access is private to ensure only one instance exists at a time.
|
||||||
|
/// </summary>
|
||||||
|
private ServiceHost() : base(new StdioServerChannel())
|
||||||
|
{
|
||||||
|
// Initialize the shutdown activities
|
||||||
|
shutdownCallbacks = new List<ShutdownCallback>();
|
||||||
|
initializeCallbacks = new List<InitializeCallback>();
|
||||||
|
|
||||||
|
// Register the requests that this service host will handle
|
||||||
|
this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest);
|
||||||
|
this.SetRequestHandler(ShutdownRequest.Type, this.HandleShutdownRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Member Variables
|
||||||
|
|
||||||
|
public delegate Task ShutdownCallback(object shutdownParams, RequestContext<object> shutdownRequestContext);
|
||||||
|
|
||||||
|
public delegate Task InitializeCallback(InitializeRequest startupParams, RequestContext<InitializeResult> requestContext);
|
||||||
|
|
||||||
|
private readonly List<ShutdownCallback> shutdownCallbacks;
|
||||||
|
|
||||||
|
private readonly List<InitializeCallback> initializeCallbacks;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a new callback to be called when the shutdown request is submitted
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback">Callback to perform when a shutdown request is submitted</param>
|
||||||
|
public void RegisterShutdownTask(ShutdownCallback callback)
|
||||||
|
{
|
||||||
|
shutdownCallbacks.Add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a new method to be called when the initialize request is submitted
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback">Callback to perform when an initialize request is submitted</param>
|
||||||
|
public void RegisterInitializeTask(InitializeCallback callback)
|
||||||
|
{
|
||||||
|
initializeCallbacks.Add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Request Handlers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the shutdown event for the Language Server
|
||||||
|
/// </summary>
|
||||||
|
private async Task HandleShutdownRequest(object shutdownParams, RequestContext<object> requestContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Normal, "Service host is shutting down...");
|
||||||
|
|
||||||
|
// Call all the shutdown methods provided by the service components
|
||||||
|
Task[] shutdownTasks = shutdownCallbacks.Select(t => t(shutdownParams, requestContext)).ToArray();
|
||||||
|
await Task.WhenAll(shutdownTasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the initialization request
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="initializeParams"></param>
|
||||||
|
/// <param name="requestContext"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task HandleInitializeRequest(InitializeRequest initializeParams, RequestContext<InitializeResult> requestContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleInitializationRequest");
|
||||||
|
|
||||||
|
// Call all tasks that registered on the initialize request
|
||||||
|
var initializeTasks = initializeCallbacks.Select(t => t(initializeParams, requestContext));
|
||||||
|
await Task.WhenAll(initializeTasks);
|
||||||
|
|
||||||
|
// TODO: Figure out where this needs to go to be agnostic of the language
|
||||||
|
|
||||||
|
// Send back what this server can do
|
||||||
|
await requestContext.SendResult(
|
||||||
|
new InitializeResult
|
||||||
|
{
|
||||||
|
Capabilities = new ServerCapabilities
|
||||||
|
{
|
||||||
|
TextDocumentSync = TextDocumentSyncKind.Incremental,
|
||||||
|
DefinitionProvider = true,
|
||||||
|
ReferencesProvider = true,
|
||||||
|
DocumentHighlightProvider = true,
|
||||||
|
DocumentSymbolProvider = true,
|
||||||
|
WorkspaceSymbolProvider = true,
|
||||||
|
HoverProvider = true,
|
||||||
|
CompletionProvider = new CompletionOptions
|
||||||
|
{
|
||||||
|
ResolveProvider = true,
|
||||||
|
TriggerCharacters = new string[] { ".", "-", ":", "\\" }
|
||||||
|
},
|
||||||
|
SignatureHelpProvider = new SignatureHelpOptions
|
||||||
|
{
|
||||||
|
TriggerCharacters = new string[] { " " } // TODO: Other characters here?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/ServiceHost/Hosting/ServiceHostBase.cs
Normal file
47
src/ServiceHost/Hosting/ServiceHostBase.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Channel;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.Hosting
|
||||||
|
{
|
||||||
|
public abstract class ServiceHostBase : ProtocolEndpoint
|
||||||
|
{
|
||||||
|
private bool isStarted;
|
||||||
|
private TaskCompletionSource<bool> serverExitedTask;
|
||||||
|
|
||||||
|
protected ServiceHostBase(ChannelBase serverChannel) :
|
||||||
|
base(serverChannel, MessageProtocolType.LanguageServer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Task OnStart()
|
||||||
|
{
|
||||||
|
// Register handlers for server lifetime messages
|
||||||
|
|
||||||
|
this.SetEventHandler(ExitNotification.Type, this.HandleExitNotification);
|
||||||
|
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleExitNotification(
|
||||||
|
object exitParams,
|
||||||
|
EventContext eventContext)
|
||||||
|
{
|
||||||
|
// Stop the server channel
|
||||||
|
await this.Stop();
|
||||||
|
|
||||||
|
// Notify any waiter that the server has exited
|
||||||
|
if (this.serverExitedTask != null)
|
||||||
|
{
|
||||||
|
this.serverExitedTask.SetResult(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -3,40 +3,29 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Connection;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.LanguageServer;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.LanguageSupport
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main class for Autocomplete functionality
|
/// Main class for Autocomplete functionality
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AutoCompleteService
|
public class AutoCompleteService
|
||||||
{
|
{
|
||||||
|
#region Singleton Instance Implementation
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Singleton service instance
|
/// Singleton service instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static Lazy<AutoCompleteService> instance
|
private static Lazy<AutoCompleteService> instance
|
||||||
= new Lazy<AutoCompleteService>(() => new AutoCompleteService());
|
= new Lazy<AutoCompleteService>(() => new AutoCompleteService());
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The current autocomplete candidate list
|
|
||||||
/// </summary>
|
|
||||||
private IEnumerable<string> autoCompleteList;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current autocomplete candidate list
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<string> AutoCompleteList
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return this.autoCompleteList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the singleton service instance
|
/// Gets the singleton service instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -48,13 +37,35 @@ namespace Microsoft.SqlTools.LanguageSupport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default, parameterless constructor.
|
||||||
|
/// TODO: Figure out how to make this truely singleton even with dependency injection for tests
|
||||||
|
/// </summary>
|
||||||
|
public AutoCompleteService()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current autocomplete candidate list
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<string> AutoCompleteList { get; private set; }
|
||||||
|
|
||||||
|
public void InitializeService(ServiceHost serviceHost)
|
||||||
|
{
|
||||||
|
// Register a callback for when a connection is created
|
||||||
|
ConnectionService.Instance.RegisterOnConnectionTask(UpdateAutoCompleteCache);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Update the cached autocomplete candidate list when the user connects to a database
|
/// Update the cached autocomplete candidate list when the user connects to a database
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="connection"></param>
|
/// <param name="connection"></param>
|
||||||
public void UpdateAutoCompleteCache(ISqlConnection connection)
|
public async Task UpdateAutoCompleteCache(ISqlConnection connection)
|
||||||
{
|
{
|
||||||
this.autoCompleteList = connection.GetServerObjects();
|
AutoCompleteList = connection.GetServerObjects();
|
||||||
|
await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -4,9 +4,10 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||||
{
|
{
|
||||||
public class CompletionRequest
|
public class CompletionRequest
|
||||||
{
|
{
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||||
{
|
{
|
||||||
public class DefinitionRequest
|
public class DefinitionRequest
|
||||||
{
|
{
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||||
{
|
{
|
||||||
public class PublishDiagnosticsNotification
|
public class PublishDiagnosticsNotification
|
||||||
{
|
{
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||||
{
|
{
|
||||||
public enum DocumentHighlightKind
|
public enum DocumentHighlightKind
|
||||||
{
|
{
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||||
{
|
{
|
||||||
public class ExpandAliasRequest
|
public class ExpandAliasRequest
|
||||||
{
|
{
|
||||||
@@ -3,10 +3,10 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||||
{
|
{
|
||||||
public class FindModuleRequest
|
public class FindModuleRequest
|
||||||
{
|
{
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||||
{
|
{
|
||||||
public class MarkedString
|
public class MarkedString
|
||||||
{
|
{
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||||
{
|
{
|
||||||
class InstallModuleRequest
|
class InstallModuleRequest
|
||||||
{
|
{
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||||
{
|
{
|
||||||
public class ReferencesRequest
|
public class ReferencesRequest
|
||||||
{
|
{
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||||
{
|
{
|
||||||
public class ShowOnlineHelpRequest
|
public class ShowOnlineHelpRequest
|
||||||
{
|
{
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts
|
||||||
{
|
{
|
||||||
public class SignatureHelpRequest
|
public class SignatureHelpRequest
|
||||||
{
|
{
|
||||||
442
src/ServiceHost/LanguageServices/LanguageService.cs
Normal file
442
src/ServiceHost/LanguageServices/LanguageService.cs
Normal file
@@ -0,0 +1,442 @@
|
|||||||
|
//
|
||||||
|
// 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;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.EditorServices.Utility;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.SqlServer.Management.SqlParser.Parser;
|
||||||
|
using Location = Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts.Location;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.LanguageServices
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Main class for Language Service functionality including anything that reqires knowledge of
|
||||||
|
/// the language to perfom, such as definitions, intellisense, etc.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class LanguageService
|
||||||
|
{
|
||||||
|
|
||||||
|
#region Singleton Instance Implementation
|
||||||
|
|
||||||
|
private static readonly Lazy<LanguageService> instance = new Lazy<LanguageService>(() => new LanguageService());
|
||||||
|
|
||||||
|
public static LanguageService Instance
|
||||||
|
{
|
||||||
|
get { return instance.Value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default, parameterless constructor.
|
||||||
|
/// TODO: Figure out how to make this truely singleton even with dependency injection for tests
|
||||||
|
/// </summary>
|
||||||
|
public LanguageService()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
private static CancellationTokenSource ExistingRequestCancellation { get; set; }
|
||||||
|
|
||||||
|
private SqlToolsSettings CurrentSettings
|
||||||
|
{
|
||||||
|
get { return WorkspaceService<SqlToolsSettings>.Instance.CurrentSettings; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Workspace CurrentWorkspace
|
||||||
|
{
|
||||||
|
get { return WorkspaceService<SqlToolsSettings>.Instance.Workspace; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the current SQL Tools context
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
private SqlToolsContext Context { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The cached parse result from previous incremental parse
|
||||||
|
/// </summary>
|
||||||
|
private ParseResult prevParseResult;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
|
||||||
|
public void InitializeService(ServiceHost serviceHost, SqlToolsContext context)
|
||||||
|
{
|
||||||
|
// Register the requests that this service will handle
|
||||||
|
serviceHost.SetRequestHandler(DefinitionRequest.Type, HandleDefinitionRequest);
|
||||||
|
serviceHost.SetRequestHandler(ReferencesRequest.Type, HandleReferencesRequest);
|
||||||
|
serviceHost.SetRequestHandler(CompletionRequest.Type, HandleCompletionRequest);
|
||||||
|
serviceHost.SetRequestHandler(CompletionResolveRequest.Type, HandleCompletionResolveRequest);
|
||||||
|
serviceHost.SetRequestHandler(SignatureHelpRequest.Type, HandleSignatureHelpRequest);
|
||||||
|
serviceHost.SetRequestHandler(DocumentHighlightRequest.Type, HandleDocumentHighlightRequest);
|
||||||
|
serviceHost.SetRequestHandler(HoverRequest.Type, HandleHoverRequest);
|
||||||
|
serviceHost.SetRequestHandler(DocumentSymbolRequest.Type, HandleDocumentSymbolRequest);
|
||||||
|
serviceHost.SetRequestHandler(WorkspaceSymbolRequest.Type, HandleWorkspaceSymbolRequest);
|
||||||
|
|
||||||
|
// Register a no-op shutdown task for validation of the shutdown logic
|
||||||
|
serviceHost.RegisterShutdownTask(async (shutdownParams, shutdownRequestContext) =>
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "Shutting down language service");
|
||||||
|
await Task.FromResult(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register the configuration update handler
|
||||||
|
WorkspaceService<SqlToolsSettings>.Instance.RegisterConfigChangeCallback(HandleDidChangeConfigurationNotification);
|
||||||
|
|
||||||
|
// Store the SqlToolsContext for future use
|
||||||
|
Context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of semantic diagnostic marks for the provided script file
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scriptFile"></param>
|
||||||
|
public ScriptFileMarker[] GetSemanticMarkers(ScriptFile scriptFile)
|
||||||
|
{
|
||||||
|
// parse current SQL file contents to retrieve a list of errors
|
||||||
|
ParseOptions parseOptions = new ParseOptions();
|
||||||
|
ParseResult parseResult = Parser.IncrementalParse(
|
||||||
|
scriptFile.Contents,
|
||||||
|
prevParseResult,
|
||||||
|
parseOptions);
|
||||||
|
|
||||||
|
// save previous result for next incremental parse
|
||||||
|
this.prevParseResult = parseResult;
|
||||||
|
|
||||||
|
// build a list of SQL script file markers from the errors
|
||||||
|
List<ScriptFileMarker> markers = new List<ScriptFileMarker>();
|
||||||
|
foreach (var error in parseResult.Errors)
|
||||||
|
{
|
||||||
|
markers.Add(new ScriptFileMarker()
|
||||||
|
{
|
||||||
|
Message = error.Message,
|
||||||
|
Level = ScriptFileMarkerLevel.Error,
|
||||||
|
ScriptRegion = new ScriptRegion()
|
||||||
|
{
|
||||||
|
File = scriptFile.FilePath,
|
||||||
|
StartLineNumber = error.Start.LineNumber,
|
||||||
|
StartColumnNumber = error.Start.ColumnNumber,
|
||||||
|
StartOffset = 0,
|
||||||
|
EndLineNumber = error.End.LineNumber,
|
||||||
|
EndColumnNumber = error.End.ColumnNumber,
|
||||||
|
EndOffset = 0
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return markers.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Request Handlers
|
||||||
|
|
||||||
|
private static async Task HandleDefinitionRequest(
|
||||||
|
TextDocumentPosition textDocumentPosition,
|
||||||
|
RequestContext<Location[]> requestContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleDefinitionRequest");
|
||||||
|
await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task HandleReferencesRequest(
|
||||||
|
ReferencesParams referencesParams,
|
||||||
|
RequestContext<Location[]> requestContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleReferencesRequest");
|
||||||
|
await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task HandleCompletionRequest(
|
||||||
|
TextDocumentPosition textDocumentPosition,
|
||||||
|
RequestContext<CompletionItem[]> requestContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleCompletionRequest");
|
||||||
|
await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task HandleCompletionResolveRequest(
|
||||||
|
CompletionItem completionItem,
|
||||||
|
RequestContext<CompletionItem> requestContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleCompletionResolveRequest");
|
||||||
|
await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task HandleSignatureHelpRequest(
|
||||||
|
TextDocumentPosition textDocumentPosition,
|
||||||
|
RequestContext<SignatureHelp> requestContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleSignatureHelpRequest");
|
||||||
|
await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task HandleDocumentHighlightRequest(
|
||||||
|
TextDocumentPosition textDocumentPosition,
|
||||||
|
RequestContext<DocumentHighlight[]> requestContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleDocumentHighlightRequest");
|
||||||
|
await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task HandleHoverRequest(
|
||||||
|
TextDocumentPosition textDocumentPosition,
|
||||||
|
RequestContext<Hover> requestContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleHoverRequest");
|
||||||
|
await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task HandleDocumentSymbolRequest(
|
||||||
|
TextDocumentIdentifier textDocumentIdentifier,
|
||||||
|
RequestContext<SymbolInformation[]> requestContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleDocumentSymbolRequest");
|
||||||
|
await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task HandleWorkspaceSymbolRequest(
|
||||||
|
WorkspaceSymbolParams workspaceSymbolParams,
|
||||||
|
RequestContext<SymbolInformation[]> requestContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleWorkspaceSymbolRequest");
|
||||||
|
await Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Handlers for Events from Other Services
|
||||||
|
|
||||||
|
public async Task HandleDidChangeConfigurationNotification(
|
||||||
|
SqlToolsSettings newSettings,
|
||||||
|
SqlToolsSettings oldSettings,
|
||||||
|
EventContext eventContext)
|
||||||
|
{
|
||||||
|
// If script analysis settings have changed we need to clear & possibly update the current diagnostic records.
|
||||||
|
bool oldScriptAnalysisEnabled = oldSettings.ScriptAnalysis.Enable.HasValue;
|
||||||
|
if ((oldScriptAnalysisEnabled != newSettings.ScriptAnalysis.Enable))
|
||||||
|
{
|
||||||
|
// If the user just turned off script analysis or changed the settings path, send a diagnostics
|
||||||
|
// event to clear the analysis markers that they already have.
|
||||||
|
if (!newSettings.ScriptAnalysis.Enable.Value)
|
||||||
|
{
|
||||||
|
ScriptFileMarker[] emptyAnalysisDiagnostics = new ScriptFileMarker[0];
|
||||||
|
|
||||||
|
foreach (var scriptFile in WorkspaceService<SqlToolsSettings>.Instance.Workspace.GetOpenedFiles())
|
||||||
|
{
|
||||||
|
await PublishScriptDiagnostics(scriptFile, emptyAnalysisDiagnostics, eventContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await this.RunScriptDiagnostics(CurrentWorkspace.GetOpenedFiles(), eventContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the settings in the current
|
||||||
|
CurrentSettings.EnableProfileLoading = newSettings.EnableProfileLoading;
|
||||||
|
CurrentSettings.ScriptAnalysis.Update(newSettings.ScriptAnalysis, CurrentWorkspace.WorkspacePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Helpers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs script diagnostics on changed files
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filesToAnalyze"></param>
|
||||||
|
/// <param name="eventContext"></param>
|
||||||
|
private Task RunScriptDiagnostics(ScriptFile[] filesToAnalyze, EventContext eventContext)
|
||||||
|
{
|
||||||
|
if (!CurrentSettings.ScriptAnalysis.Enable.Value)
|
||||||
|
{
|
||||||
|
// If the user has disabled script analysis, skip it entirely
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's an existing task, attempt to cancel it
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (ExistingRequestCancellation != null)
|
||||||
|
{
|
||||||
|
// Try to cancel the request
|
||||||
|
ExistingRequestCancellation.Cancel();
|
||||||
|
|
||||||
|
// If cancellation didn't throw an exception,
|
||||||
|
// clean up the existing token
|
||||||
|
ExistingRequestCancellation.Dispose();
|
||||||
|
ExistingRequestCancellation = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Write(
|
||||||
|
LogLevel.Error,
|
||||||
|
String.Format(
|
||||||
|
"Exception while cancelling analysis task:\n\n{0}",
|
||||||
|
e.ToString()));
|
||||||
|
|
||||||
|
TaskCompletionSource<bool> cancelTask = new TaskCompletionSource<bool>();
|
||||||
|
cancelTask.SetCanceled();
|
||||||
|
return cancelTask.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a fresh cancellation token and then start the task.
|
||||||
|
// We create this on a different TaskScheduler so that we
|
||||||
|
// don't block the main message loop thread.
|
||||||
|
ExistingRequestCancellation = new CancellationTokenSource();
|
||||||
|
Task.Factory.StartNew(
|
||||||
|
() =>
|
||||||
|
DelayThenInvokeDiagnostics(
|
||||||
|
750,
|
||||||
|
filesToAnalyze,
|
||||||
|
eventContext,
|
||||||
|
ExistingRequestCancellation.Token),
|
||||||
|
CancellationToken.None,
|
||||||
|
TaskCreationOptions.None,
|
||||||
|
TaskScheduler.Default);
|
||||||
|
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actually run the script diagnostics after waiting for some small delay
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="delayMilliseconds"></param>
|
||||||
|
/// <param name="filesToAnalyze"></param>
|
||||||
|
/// <param name="eventContext"></param>
|
||||||
|
/// <param name="cancellationToken"></param>
|
||||||
|
private async Task DelayThenInvokeDiagnostics(
|
||||||
|
int delayMilliseconds,
|
||||||
|
ScriptFile[] filesToAnalyze,
|
||||||
|
EventContext eventContext,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
// First of all, wait for the desired delay period before
|
||||||
|
// analyzing the provided list of files
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(delayMilliseconds, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
// If the task is cancelled, exit directly
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've made it past the delay period then we don't care
|
||||||
|
// about the cancellation token anymore. This could happen
|
||||||
|
// when the user stops typing for long enough that the delay
|
||||||
|
// period ends but then starts typing while analysis is going
|
||||||
|
// on. It makes sense to send back the results from the first
|
||||||
|
// delay period while the second one is ticking away.
|
||||||
|
|
||||||
|
// Get the requested files
|
||||||
|
foreach (ScriptFile scriptFile in filesToAnalyze)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "Analyzing script file: " + scriptFile.FilePath);
|
||||||
|
ScriptFileMarker[] semanticMarkers = GetSemanticMarkers(scriptFile);
|
||||||
|
Logger.Write(LogLevel.Verbose, "Analysis complete.");
|
||||||
|
|
||||||
|
await PublishScriptDiagnostics(scriptFile, semanticMarkers, eventContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Send the diagnostic results back to the host application
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scriptFile"></param>
|
||||||
|
/// <param name="semanticMarkers"></param>
|
||||||
|
/// <param name="eventContext"></param>
|
||||||
|
private static async Task PublishScriptDiagnostics(
|
||||||
|
ScriptFile scriptFile,
|
||||||
|
ScriptFileMarker[] semanticMarkers,
|
||||||
|
EventContext eventContext)
|
||||||
|
{
|
||||||
|
var allMarkers = scriptFile.SyntaxMarkers != null
|
||||||
|
? scriptFile.SyntaxMarkers.Concat(semanticMarkers)
|
||||||
|
: semanticMarkers;
|
||||||
|
|
||||||
|
// Always send syntax and semantic errors. We want to
|
||||||
|
// make sure no out-of-date markers are being displayed.
|
||||||
|
await eventContext.SendEvent(
|
||||||
|
PublishDiagnosticsNotification.Type,
|
||||||
|
new PublishDiagnosticsNotification
|
||||||
|
{
|
||||||
|
Uri = scriptFile.ClientFilePath,
|
||||||
|
Diagnostics =
|
||||||
|
allMarkers
|
||||||
|
.Select(GetDiagnosticFromMarker)
|
||||||
|
.ToArray()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a ScriptFileMarker to a Diagnostic that is Language Service compatible
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scriptFileMarker"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static Diagnostic GetDiagnosticFromMarker(ScriptFileMarker scriptFileMarker)
|
||||||
|
{
|
||||||
|
return new Diagnostic
|
||||||
|
{
|
||||||
|
Severity = MapDiagnosticSeverity(scriptFileMarker.Level),
|
||||||
|
Message = scriptFileMarker.Message,
|
||||||
|
Range = new Range
|
||||||
|
{
|
||||||
|
// TODO: What offsets should I use?
|
||||||
|
Start = new Position
|
||||||
|
{
|
||||||
|
Line = scriptFileMarker.ScriptRegion.StartLineNumber - 1,
|
||||||
|
Character = scriptFileMarker.ScriptRegion.StartColumnNumber - 1
|
||||||
|
},
|
||||||
|
End = new Position
|
||||||
|
{
|
||||||
|
Line = scriptFileMarker.ScriptRegion.EndLineNumber - 1,
|
||||||
|
Character = scriptFileMarker.ScriptRegion.EndColumnNumber - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Map ScriptFileMarker severity to Diagnostic severity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="markerLevel"></param>
|
||||||
|
private static DiagnosticSeverity MapDiagnosticSeverity(ScriptFileMarkerLevel markerLevel)
|
||||||
|
{
|
||||||
|
switch (markerLevel)
|
||||||
|
{
|
||||||
|
case ScriptFileMarkerLevel.Error:
|
||||||
|
return DiagnosticSeverity.Error;
|
||||||
|
|
||||||
|
case ScriptFileMarkerLevel.Warning:
|
||||||
|
return DiagnosticSeverity.Warning;
|
||||||
|
|
||||||
|
case ScriptFileMarkerLevel.Information:
|
||||||
|
return DiagnosticSeverity.Information;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return DiagnosticSeverity.Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) Microsoft. All rights reserved.
|
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Session;
|
|
||||||
using Microsoft.SqlServer.Management.SqlParser.Parser;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.LanguageSupport
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Main class for Language Service functionality
|
|
||||||
/// </summary>
|
|
||||||
public class LanguageService
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The cached parse result from previous incremental parse
|
|
||||||
/// </summary>
|
|
||||||
private ParseResult prevParseResult;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the current SQL Tools context
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
private SqlToolsContext Context { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor for the Language Service class
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
public LanguageService(SqlToolsContext context)
|
|
||||||
{
|
|
||||||
this.Context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list of semantic diagnostic marks for the provided script file
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="scriptFile"></param>
|
|
||||||
public ScriptFileMarker[] GetSemanticMarkers(ScriptFile scriptFile)
|
|
||||||
{
|
|
||||||
// parse current SQL file contents to retrieve a list of errors
|
|
||||||
ParseOptions parseOptions = new ParseOptions();
|
|
||||||
ParseResult parseResult = Parser.IncrementalParse(
|
|
||||||
scriptFile.Contents,
|
|
||||||
prevParseResult,
|
|
||||||
parseOptions);
|
|
||||||
|
|
||||||
// save previous result for next incremental parse
|
|
||||||
this.prevParseResult = parseResult;
|
|
||||||
|
|
||||||
// build a list of SQL script file markers from the errors
|
|
||||||
List<ScriptFileMarker> markers = new List<ScriptFileMarker>();
|
|
||||||
foreach (var error in parseResult.Errors)
|
|
||||||
{
|
|
||||||
markers.Add(new ScriptFileMarker()
|
|
||||||
{
|
|
||||||
Message = error.Message,
|
|
||||||
Level = ScriptFileMarkerLevel.Error,
|
|
||||||
ScriptRegion = new ScriptRegion()
|
|
||||||
{
|
|
||||||
File = scriptFile.FilePath,
|
|
||||||
StartLineNumber = error.Start.LineNumber,
|
|
||||||
StartColumnNumber = error.Start.ColumnNumber,
|
|
||||||
StartOffset = 0,
|
|
||||||
EndLineNumber = error.End.LineNumber,
|
|
||||||
EndColumnNumber = error.End.ColumnNumber,
|
|
||||||
EndOffset = 0
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return markers.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
// Copyright (c) Microsoft. All rights reserved.
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.Server;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Session;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Utility;
|
using Microsoft.SqlTools.EditorServices.Utility;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceHost
|
namespace Microsoft.SqlTools.ServiceLayer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main application class for SQL Tools API Service Host executable
|
/// Main application class for SQL Tools API Service Host executable
|
||||||
@@ -25,16 +27,24 @@ namespace Microsoft.SqlTools.ServiceHost
|
|||||||
|
|
||||||
const string hostName = "SQL Tools Service Host";
|
const string hostName = "SQL Tools Service Host";
|
||||||
const string hostProfileId = "SQLToolsService";
|
const string hostProfileId = "SQLToolsService";
|
||||||
Version hostVersion = new Version(1,0);
|
Version hostVersion = new Version(1,0);
|
||||||
|
|
||||||
// set up the host details and profile paths
|
// set up the host details and profile paths
|
||||||
var hostDetails = new HostDetails(hostName, hostProfileId, hostVersion);
|
var hostDetails = new HostDetails(hostName, hostProfileId, hostVersion);
|
||||||
var profilePaths = new ProfilePaths(hostProfileId, "baseAllUsersPath", "baseCurrentUserPath");
|
var profilePaths = new ProfilePaths(hostProfileId, "baseAllUsersPath", "baseCurrentUserPath");
|
||||||
|
SqlToolsContext sqlToolsContext = new SqlToolsContext(hostDetails, profilePaths);
|
||||||
|
|
||||||
// create and run the language server
|
// Grab the instance of the service host
|
||||||
var languageServer = new LanguageServer(hostDetails, profilePaths);
|
ServiceHost serviceHost = ServiceHost.Instance;
|
||||||
languageServer.Start().Wait();
|
|
||||||
languageServer.WaitForExit();
|
// Initialize the services that will be hosted here
|
||||||
|
WorkspaceService<SqlToolsSettings>.Instance.InitializeService(serviceHost);
|
||||||
|
AutoCompleteService.Instance.InitializeService(serviceHost);
|
||||||
|
LanguageService.Instance.InitializeService(serviceHost, sqlToolsContext);
|
||||||
|
|
||||||
|
// Start the service
|
||||||
|
serviceHost.Start().Wait();
|
||||||
|
serviceHost.WaitForExit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,583 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) Microsoft. All rights reserved.
|
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.LanguageServer;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Channel;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Session;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Utility;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Linq;
|
|
||||||
using System;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Connection;
|
|
||||||
using Microsoft.SqlTools.LanguageSupport;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.Server
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// SQL Tools VS Code Language Server request handler
|
|
||||||
/// </summary>
|
|
||||||
public class LanguageServer : LanguageServerBase
|
|
||||||
{
|
|
||||||
private static CancellationTokenSource existingRequestCancellation;
|
|
||||||
|
|
||||||
private LanguageServerSettings currentSettings = new LanguageServerSettings();
|
|
||||||
|
|
||||||
private EditorSession editorSession;
|
|
||||||
|
|
||||||
/// <param name="hostDetails">
|
|
||||||
/// Provides details about the host application.
|
|
||||||
/// </param>
|
|
||||||
public LanguageServer(HostDetails hostDetails, ProfilePaths profilePaths)
|
|
||||||
: base(new StdioServerChannel())
|
|
||||||
{
|
|
||||||
this.editorSession = new EditorSession();
|
|
||||||
this.editorSession.StartSession(hostDetails, profilePaths);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize the VS Code request/response callbacks
|
|
||||||
/// </summary>
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
// Register all supported message types
|
|
||||||
this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest);
|
|
||||||
this.SetEventHandler(DidChangeTextDocumentNotification.Type, this.HandleDidChangeTextDocumentNotification);
|
|
||||||
this.SetEventHandler(DidOpenTextDocumentNotification.Type, this.HandleDidOpenTextDocumentNotification);
|
|
||||||
this.SetEventHandler(DidCloseTextDocumentNotification.Type, this.HandleDidCloseTextDocumentNotification);
|
|
||||||
this.SetEventHandler(DidChangeConfigurationNotification<LanguageServerSettingsWrapper>.Type, this.HandleDidChangeConfigurationNotification);
|
|
||||||
|
|
||||||
this.SetRequestHandler(DefinitionRequest.Type, this.HandleDefinitionRequest);
|
|
||||||
this.SetRequestHandler(ReferencesRequest.Type, this.HandleReferencesRequest);
|
|
||||||
this.SetRequestHandler(CompletionRequest.Type, this.HandleCompletionRequest);
|
|
||||||
this.SetRequestHandler(CompletionResolveRequest.Type, this.HandleCompletionResolveRequest);
|
|
||||||
this.SetRequestHandler(SignatureHelpRequest.Type, this.HandleSignatureHelpRequest);
|
|
||||||
this.SetRequestHandler(DocumentHighlightRequest.Type, this.HandleDocumentHighlightRequest);
|
|
||||||
this.SetRequestHandler(HoverRequest.Type, this.HandleHoverRequest);
|
|
||||||
this.SetRequestHandler(DocumentSymbolRequest.Type, this.HandleDocumentSymbolRequest);
|
|
||||||
this.SetRequestHandler(WorkspaceSymbolRequest.Type, this.HandleWorkspaceSymbolRequest);
|
|
||||||
|
|
||||||
this.SetRequestHandler(ConnectionRequest.Type, this.HandleConnectRequest);
|
|
||||||
|
|
||||||
// register an OnConnection callback
|
|
||||||
ConnectionService.Instance.RegisterOnConnectionTask(OnConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Callback for when a user connection is done processing
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sqlConnection"></param>
|
|
||||||
public Task OnConnection(ISqlConnection sqlConnection)
|
|
||||||
{
|
|
||||||
AutoCompleteService.Instance.UpdateAutoCompleteCache(sqlConnection);
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles the shutdown event for the Language Server
|
|
||||||
/// </summary>
|
|
||||||
protected override async Task Shutdown()
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Normal, "Language service is shutting down...");
|
|
||||||
|
|
||||||
if (this.editorSession != null)
|
|
||||||
{
|
|
||||||
this.editorSession.Dispose();
|
|
||||||
this.editorSession = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles the initialization request
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="initializeParams"></param>
|
|
||||||
/// <param name="requestContext"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected async Task HandleInitializeRequest(
|
|
||||||
InitializeRequest initializeParams,
|
|
||||||
RequestContext<InitializeResult> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDidChangeTextDocumentNotification");
|
|
||||||
|
|
||||||
// Grab the workspace path from the parameters
|
|
||||||
editorSession.Workspace.WorkspacePath = initializeParams.RootPath;
|
|
||||||
|
|
||||||
await requestContext.SendResult(
|
|
||||||
new InitializeResult
|
|
||||||
{
|
|
||||||
Capabilities = new ServerCapabilities
|
|
||||||
{
|
|
||||||
TextDocumentSync = TextDocumentSyncKind.Incremental,
|
|
||||||
DefinitionProvider = true,
|
|
||||||
ReferencesProvider = true,
|
|
||||||
DocumentHighlightProvider = true,
|
|
||||||
DocumentSymbolProvider = true,
|
|
||||||
WorkspaceSymbolProvider = true,
|
|
||||||
HoverProvider = true,
|
|
||||||
CompletionProvider = new CompletionOptions
|
|
||||||
{
|
|
||||||
ResolveProvider = true,
|
|
||||||
TriggerCharacters = new string[] { ".", "-", ":", "\\" }
|
|
||||||
},
|
|
||||||
SignatureHelpProvider = new SignatureHelpOptions
|
|
||||||
{
|
|
||||||
TriggerCharacters = new string[] { " " } // TODO: Other characters here?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles text document change events
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="textChangeParams"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected async Task HandleDidChangeTextDocumentNotification(
|
|
||||||
DidChangeTextDocumentParams textChangeParams,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
StringBuilder msg = new StringBuilder();
|
|
||||||
msg.Append("HandleDidChangeTextDocumentNotification");
|
|
||||||
List<ScriptFile> changedFiles = new List<ScriptFile>();
|
|
||||||
|
|
||||||
// A text change notification can batch multiple change requests
|
|
||||||
foreach (var textChange in textChangeParams.ContentChanges)
|
|
||||||
{
|
|
||||||
string fileUri = textChangeParams.Uri ?? textChangeParams.TextDocument.Uri;
|
|
||||||
msg.AppendLine();
|
|
||||||
msg.Append(" File: ");
|
|
||||||
msg.Append(fileUri);
|
|
||||||
|
|
||||||
ScriptFile changedFile = editorSession.Workspace.GetFile(fileUri);
|
|
||||||
|
|
||||||
changedFile.ApplyChange(
|
|
||||||
GetFileChangeDetails(
|
|
||||||
textChange.Range.Value,
|
|
||||||
textChange.Text));
|
|
||||||
|
|
||||||
changedFiles.Add(changedFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Write(LogLevel.Verbose, msg.ToString());
|
|
||||||
|
|
||||||
await this.RunScriptDiagnostics(
|
|
||||||
changedFiles.ToArray(),
|
|
||||||
editorSession,
|
|
||||||
eventContext);
|
|
||||||
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle the file open notification
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="openParams"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
protected Task HandleDidOpenTextDocumentNotification(
|
|
||||||
DidOpenTextDocumentNotification openParams,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDidOpenTextDocumentNotification");
|
|
||||||
|
|
||||||
// read the SQL file contents into the ScriptFile
|
|
||||||
ScriptFile openedFile =
|
|
||||||
editorSession.Workspace.GetFileBuffer(
|
|
||||||
openParams.Uri,
|
|
||||||
openParams.Text);
|
|
||||||
|
|
||||||
// run diagnostics on the opened file
|
|
||||||
this.RunScriptDiagnostics(
|
|
||||||
new ScriptFile[] { openedFile },
|
|
||||||
editorSession,
|
|
||||||
eventContext);
|
|
||||||
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle the close document notication
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="closeParams"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
protected Task HandleDidCloseTextDocumentNotification(
|
|
||||||
TextDocumentIdentifier closeParams,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDidCloseTextDocumentNotification");
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles the configuration change event
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configChangeParams"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
protected async Task HandleDidChangeConfigurationNotification(
|
|
||||||
DidChangeConfigurationParams<LanguageServerSettingsWrapper> configChangeParams,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDidChangeConfigurationNotification");
|
|
||||||
|
|
||||||
bool oldLoadProfiles = this.currentSettings.EnableProfileLoading;
|
|
||||||
bool oldScriptAnalysisEnabled =
|
|
||||||
this.currentSettings.ScriptAnalysis.Enable.HasValue;
|
|
||||||
string oldScriptAnalysisSettingsPath =
|
|
||||||
this.currentSettings.ScriptAnalysis.SettingsPath;
|
|
||||||
|
|
||||||
this.currentSettings.Update(
|
|
||||||
configChangeParams.Settings.SqlTools,
|
|
||||||
this.editorSession.Workspace.WorkspacePath);
|
|
||||||
|
|
||||||
// If script analysis settings have changed we need to clear & possibly update the current diagnostic records.
|
|
||||||
if ((oldScriptAnalysisEnabled != this.currentSettings.ScriptAnalysis.Enable))
|
|
||||||
{
|
|
||||||
// If the user just turned off script analysis or changed the settings path, send a diagnostics
|
|
||||||
// event to clear the analysis markers that they already have.
|
|
||||||
if (!this.currentSettings.ScriptAnalysis.Enable.Value)
|
|
||||||
{
|
|
||||||
ScriptFileMarker[] emptyAnalysisDiagnostics = new ScriptFileMarker[0];
|
|
||||||
|
|
||||||
foreach (var scriptFile in editorSession.Workspace.GetOpenedFiles())
|
|
||||||
{
|
|
||||||
await PublishScriptDiagnostics(
|
|
||||||
scriptFile,
|
|
||||||
emptyAnalysisDiagnostics,
|
|
||||||
eventContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await this.RunScriptDiagnostics(
|
|
||||||
this.editorSession.Workspace.GetOpenedFiles(),
|
|
||||||
this.editorSession,
|
|
||||||
eventContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleDefinitionRequest(
|
|
||||||
TextDocumentPosition textDocumentPosition,
|
|
||||||
RequestContext<Location[]> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDefinitionRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleReferencesRequest(
|
|
||||||
ReferencesParams referencesParams,
|
|
||||||
RequestContext<Location[]> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleReferencesRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles the completion list request
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="textDocumentPosition"></param>
|
|
||||||
/// <param name="requestContext"></param>
|
|
||||||
protected async Task HandleCompletionRequest(
|
|
||||||
TextDocumentPosition textDocumentPosition,
|
|
||||||
RequestContext<CompletionItem[]> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleCompletionRequest");
|
|
||||||
|
|
||||||
// get teh current list of completion items and return to client
|
|
||||||
var completionItems = AutoCompleteService.Instance.GetCompletionItems(textDocumentPosition);
|
|
||||||
await requestContext.SendResult(completionItems);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleCompletionResolveRequest(
|
|
||||||
CompletionItem completionItem,
|
|
||||||
RequestContext<CompletionItem> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleCompletionResolveRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleSignatureHelpRequest(
|
|
||||||
TextDocumentPosition textDocumentPosition,
|
|
||||||
RequestContext<SignatureHelp> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleSignatureHelpRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleDocumentHighlightRequest(
|
|
||||||
TextDocumentPosition textDocumentPosition,
|
|
||||||
RequestContext<DocumentHighlight[]> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDocumentHighlightRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleHoverRequest(
|
|
||||||
TextDocumentPosition textDocumentPosition,
|
|
||||||
RequestContext<Hover> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleHoverRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleDocumentSymbolRequest(
|
|
||||||
TextDocumentIdentifier textDocumentIdentifier,
|
|
||||||
RequestContext<SymbolInformation[]> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleDocumentSymbolRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async Task HandleWorkspaceSymbolRequest(
|
|
||||||
WorkspaceSymbolParams workspaceSymbolParams,
|
|
||||||
RequestContext<SymbolInformation[]> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleWorkspaceSymbolRequest");
|
|
||||||
await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handle new connection requests
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="connectionDetails"></param>
|
|
||||||
/// <param name="requestContext"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected async Task HandleConnectRequest(
|
|
||||||
ConnectionDetails connectionDetails,
|
|
||||||
RequestContext<ConnectionResult> requestContext)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "HandleConnectRequest");
|
|
||||||
|
|
||||||
// open connection base on request details
|
|
||||||
ConnectionResult result = ConnectionService.Instance.Connect(connectionDetails);
|
|
||||||
|
|
||||||
await requestContext.SendResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Runs script diagnostics on changed files
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filesToAnalyze"></param>
|
|
||||||
/// <param name="editorSession"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
private Task RunScriptDiagnostics(
|
|
||||||
ScriptFile[] filesToAnalyze,
|
|
||||||
EditorSession editorSession,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
if (!this.currentSettings.ScriptAnalysis.Enable.Value)
|
|
||||||
{
|
|
||||||
// If the user has disabled script analysis, skip it entirely
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there's an existing task, attempt to cancel it
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (existingRequestCancellation != null)
|
|
||||||
{
|
|
||||||
// Try to cancel the request
|
|
||||||
existingRequestCancellation.Cancel();
|
|
||||||
|
|
||||||
// If cancellation didn't throw an exception,
|
|
||||||
// clean up the existing token
|
|
||||||
existingRequestCancellation.Dispose();
|
|
||||||
existingRequestCancellation = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.Write(
|
|
||||||
LogLevel.Error,
|
|
||||||
string.Format(
|
|
||||||
"Exception while cancelling analysis task:\n\n{0}",
|
|
||||||
e.ToString()));
|
|
||||||
|
|
||||||
TaskCompletionSource<bool> cancelTask = new TaskCompletionSource<bool>();
|
|
||||||
cancelTask.SetCanceled();
|
|
||||||
return cancelTask.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a fresh cancellation token and then start the task.
|
|
||||||
// We create this on a different TaskScheduler so that we
|
|
||||||
// don't block the main message loop thread.
|
|
||||||
existingRequestCancellation = new CancellationTokenSource();
|
|
||||||
Task.Factory.StartNew(
|
|
||||||
() =>
|
|
||||||
DelayThenInvokeDiagnostics(
|
|
||||||
750,
|
|
||||||
filesToAnalyze,
|
|
||||||
editorSession,
|
|
||||||
eventContext,
|
|
||||||
existingRequestCancellation.Token),
|
|
||||||
CancellationToken.None,
|
|
||||||
TaskCreationOptions.None,
|
|
||||||
TaskScheduler.Default);
|
|
||||||
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Actually run the script diagnostics after waiting for some small delay
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="delayMilliseconds"></param>
|
|
||||||
/// <param name="filesToAnalyze"></param>
|
|
||||||
/// <param name="editorSession"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
/// <param name="cancellationToken"></param>
|
|
||||||
private static async Task DelayThenInvokeDiagnostics(
|
|
||||||
int delayMilliseconds,
|
|
||||||
ScriptFile[] filesToAnalyze,
|
|
||||||
EditorSession editorSession,
|
|
||||||
EventContext eventContext,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
// First of all, wait for the desired delay period before
|
|
||||||
// analyzing the provided list of files
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Task.Delay(delayMilliseconds, cancellationToken);
|
|
||||||
}
|
|
||||||
catch (TaskCanceledException)
|
|
||||||
{
|
|
||||||
// If the task is cancelled, exit directly
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we've made it past the delay period then we don't care
|
|
||||||
// about the cancellation token anymore. This could happen
|
|
||||||
// when the user stops typing for long enough that the delay
|
|
||||||
// period ends but then starts typing while analysis is going
|
|
||||||
// on. It makes sense to send back the results from the first
|
|
||||||
// delay period while the second one is ticking away.
|
|
||||||
|
|
||||||
// Get the requested files
|
|
||||||
foreach (ScriptFile scriptFile in filesToAnalyze)
|
|
||||||
{
|
|
||||||
ScriptFileMarker[] semanticMarkers = null;
|
|
||||||
if (editorSession.LanguageService != null)
|
|
||||||
{
|
|
||||||
Logger.Write(LogLevel.Verbose, "Analyzing script file: " + scriptFile.FilePath);
|
|
||||||
semanticMarkers = editorSession.LanguageService.GetSemanticMarkers(scriptFile);
|
|
||||||
Logger.Write(LogLevel.Verbose, "Analysis complete.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Semantic markers aren't available if the AnalysisService
|
|
||||||
// isn't available
|
|
||||||
semanticMarkers = new ScriptFileMarker[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
await PublishScriptDiagnostics(
|
|
||||||
scriptFile,
|
|
||||||
semanticMarkers,
|
|
||||||
eventContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Send the diagnostic results back to the host application
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="scriptFile"></param>
|
|
||||||
/// <param name="semanticMarkers"></param>
|
|
||||||
/// <param name="eventContext"></param>
|
|
||||||
private static async Task PublishScriptDiagnostics(
|
|
||||||
ScriptFile scriptFile,
|
|
||||||
ScriptFileMarker[] semanticMarkers,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
var allMarkers = scriptFile.SyntaxMarkers != null
|
|
||||||
? scriptFile.SyntaxMarkers.Concat(semanticMarkers)
|
|
||||||
: semanticMarkers;
|
|
||||||
|
|
||||||
// Always send syntax and semantic errors. We want to
|
|
||||||
// make sure no out-of-date markers are being displayed.
|
|
||||||
await eventContext.SendEvent(
|
|
||||||
PublishDiagnosticsNotification.Type,
|
|
||||||
new PublishDiagnosticsNotification
|
|
||||||
{
|
|
||||||
Uri = scriptFile.ClientFilePath,
|
|
||||||
Diagnostics =
|
|
||||||
allMarkers
|
|
||||||
.Select(GetDiagnosticFromMarker)
|
|
||||||
.ToArray()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert a ScriptFileMarker to a Diagnostic that is Language Service compatible
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="scriptFileMarker"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static Diagnostic GetDiagnosticFromMarker(ScriptFileMarker scriptFileMarker)
|
|
||||||
{
|
|
||||||
return new Diagnostic
|
|
||||||
{
|
|
||||||
Severity = MapDiagnosticSeverity(scriptFileMarker.Level),
|
|
||||||
Message = scriptFileMarker.Message,
|
|
||||||
Range = new Range
|
|
||||||
{
|
|
||||||
// TODO: What offsets should I use?
|
|
||||||
Start = new Position
|
|
||||||
{
|
|
||||||
Line = scriptFileMarker.ScriptRegion.StartLineNumber - 1,
|
|
||||||
Character = scriptFileMarker.ScriptRegion.StartColumnNumber - 1
|
|
||||||
},
|
|
||||||
End = new Position
|
|
||||||
{
|
|
||||||
Line = scriptFileMarker.ScriptRegion.EndLineNumber - 1,
|
|
||||||
Character = scriptFileMarker.ScriptRegion.EndColumnNumber - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Map ScriptFileMarker severity to Diagnostic severity
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="markerLevel"></param>
|
|
||||||
private static DiagnosticSeverity MapDiagnosticSeverity(ScriptFileMarkerLevel markerLevel)
|
|
||||||
{
|
|
||||||
switch (markerLevel)
|
|
||||||
{
|
|
||||||
case ScriptFileMarkerLevel.Error:
|
|
||||||
return DiagnosticSeverity.Error;
|
|
||||||
|
|
||||||
case ScriptFileMarkerLevel.Warning:
|
|
||||||
return DiagnosticSeverity.Warning;
|
|
||||||
|
|
||||||
case ScriptFileMarkerLevel.Information:
|
|
||||||
return DiagnosticSeverity.Information;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return DiagnosticSeverity.Error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Switch from 0-based offsets to 1 based offsets
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="changeRange"></param>
|
|
||||||
/// <param name="insertString"></param>
|
|
||||||
private static FileChange GetFileChangeDetails(Range changeRange, string insertString)
|
|
||||||
{
|
|
||||||
// The protocol's positions are zero-based so add 1 to all offsets
|
|
||||||
return new FileChange
|
|
||||||
{
|
|
||||||
InsertString = insertString,
|
|
||||||
Line = changeRange.Start.Line + 1,
|
|
||||||
Offset = changeRange.Start.Character + 1,
|
|
||||||
EndLine = changeRange.End.Line + 1,
|
|
||||||
EndOffset = changeRange.End.Character + 1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) Microsoft. All rights reserved.
|
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.LanguageServer;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Channel;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.Server
|
|
||||||
{
|
|
||||||
public abstract class LanguageServerBase : ProtocolEndpoint
|
|
||||||
{
|
|
||||||
private bool isStarted;
|
|
||||||
private ChannelBase serverChannel;
|
|
||||||
private TaskCompletionSource<bool> serverExitedTask;
|
|
||||||
|
|
||||||
public LanguageServerBase(ChannelBase serverChannel) :
|
|
||||||
base(serverChannel, MessageProtocolType.LanguageServer)
|
|
||||||
{
|
|
||||||
this.serverChannel = serverChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Task OnStart()
|
|
||||||
{
|
|
||||||
// Register handlers for server lifetime messages
|
|
||||||
this.SetRequestHandler(ShutdownRequest.Type, this.HandleShutdownRequest);
|
|
||||||
this.SetEventHandler(ExitNotification.Type, this.HandleExitNotification);
|
|
||||||
|
|
||||||
// Initialize the implementation class
|
|
||||||
this.Initialize();
|
|
||||||
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task OnStop()
|
|
||||||
{
|
|
||||||
await this.Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Overridden by the subclass to provide initialization
|
|
||||||
/// logic after the server channel is started.
|
|
||||||
/// </summary>
|
|
||||||
protected abstract void Initialize();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Can be overridden by the subclass to provide shutdown
|
|
||||||
/// logic before the server exits. Subclasses do not need
|
|
||||||
/// to invoke or return the value of the base implementation.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual Task Shutdown()
|
|
||||||
{
|
|
||||||
// No default implementation yet.
|
|
||||||
return Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleShutdownRequest(
|
|
||||||
object shutdownParams,
|
|
||||||
RequestContext<object> requestContext)
|
|
||||||
{
|
|
||||||
// Allow the implementor to shut down gracefully
|
|
||||||
await this.Shutdown();
|
|
||||||
|
|
||||||
await requestContext.SendResult(new object());
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleExitNotification(
|
|
||||||
object exitParams,
|
|
||||||
EventContext eventContext)
|
|
||||||
{
|
|
||||||
// Stop the server channel
|
|
||||||
await this.Stop();
|
|
||||||
|
|
||||||
// Notify any waiter that the server has exited
|
|
||||||
if (this.serverExitedTask != null)
|
|
||||||
{
|
|
||||||
this.serverExitedTask.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||||
@@ -7,14 +7,13 @@
|
|||||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<ProjectGuid>{0D61DC2B-DA66-441D-B9D0-F76C98F780F9}</ProjectGuid>
|
<ProjectGuid>{0D61DC2B-DA66-441D-B9D0-F76C98F780F9}</ProjectGuid>
|
||||||
<RootNamespace>Microsoft.SqlTools.ServiceHost</RootNamespace>
|
<RootNamespace>Microsoft.SqlTools.ServiceLayer</RootNamespace>
|
||||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'==''">.\obj</BaseIntermediateOutputPath>
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'==''">.\obj</BaseIntermediateOutputPath>
|
||||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
//
|
|
||||||
// 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 Microsoft.SqlTools.EditorServices.Session;
|
|
||||||
using Microsoft.SqlTools.LanguageSupport;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Manages a single session for all editor services. This
|
|
||||||
/// includes managing all open script files for the session.
|
|
||||||
/// </summary>
|
|
||||||
public class EditorSession : IDisposable
|
|
||||||
{
|
|
||||||
#region Properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the Workspace instance for this session.
|
|
||||||
/// </summary>
|
|
||||||
public Workspace Workspace { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the Language Service
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public LanguageService LanguageService { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the SqlToolsContext instance for this session.
|
|
||||||
/// </summary>
|
|
||||||
public SqlToolsContext SqlToolsContext { get; private set; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Public Methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Starts the session using the provided IConsoleHost implementation
|
|
||||||
/// for the ConsoleService.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hostDetails">
|
|
||||||
/// Provides details about the host application.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="profilePaths">
|
|
||||||
/// An object containing the profile paths for the session.
|
|
||||||
/// </param>
|
|
||||||
public void StartSession(HostDetails hostDetails, ProfilePaths profilePaths)
|
|
||||||
{
|
|
||||||
// Initialize all services
|
|
||||||
this.SqlToolsContext = new SqlToolsContext(hostDetails, profilePaths);
|
|
||||||
this.LanguageService = new LanguageService(this.SqlToolsContext);
|
|
||||||
|
|
||||||
// Create a workspace to contain open files
|
|
||||||
this.Workspace = new Workspace(this.SqlToolsContext.SqlToolsVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region IDisposable Implementation
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disposes of any Runspaces that were created for the
|
|
||||||
/// services used in this session.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright (c) Microsoft. All rights reserved.
|
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Enumerates the types of output lines that will be sent
|
|
||||||
/// to an IConsoleHost implementation.
|
|
||||||
/// </summary>
|
|
||||||
public enum OutputType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A normal output line, usually written with the or Write-Host or
|
|
||||||
/// Write-Output cmdlets.
|
|
||||||
/// </summary>
|
|
||||||
Normal,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A debug output line, written with the Write-Debug cmdlet.
|
|
||||||
/// </summary>
|
|
||||||
Debug,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A verbose output line, written with the Write-Verbose cmdlet.
|
|
||||||
/// </summary>
|
|
||||||
Verbose,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A warning output line, written with the Write-Warning cmdlet.
|
|
||||||
/// </summary>
|
|
||||||
Warning,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An error output line, written with the Write-Error cmdlet or
|
|
||||||
/// as a result of some error during SqlTools pipeline execution.
|
|
||||||
/// </summary>
|
|
||||||
Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Provides details about output that has been written to the
|
|
||||||
/// SqlTools host.
|
|
||||||
/// </summary>
|
|
||||||
public class OutputWrittenEventArgs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the text of the output.
|
|
||||||
/// </summary>
|
|
||||||
public string OutputText { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the type of the output.
|
|
||||||
/// </summary>
|
|
||||||
public OutputType OutputType { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a boolean which indicates whether a newline
|
|
||||||
/// should be written after the output.
|
|
||||||
/// </summary>
|
|
||||||
public bool IncludeNewLine { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the foreground color of the output text.
|
|
||||||
/// </summary>
|
|
||||||
public ConsoleColor ForegroundColor { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the background color of the output text.
|
|
||||||
/// </summary>
|
|
||||||
public ConsoleColor BackgroundColor { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the OutputWrittenEventArgs class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="outputText">The text of the output.</param>
|
|
||||||
/// <param name="includeNewLine">A boolean which indicates whether a newline should be written after the output.</param>
|
|
||||||
/// <param name="outputType">The type of the output.</param>
|
|
||||||
/// <param name="foregroundColor">The foreground color of the output text.</param>
|
|
||||||
/// <param name="backgroundColor">The background color of the output text.</param>
|
|
||||||
public OutputWrittenEventArgs(
|
|
||||||
string outputText,
|
|
||||||
bool includeNewLine,
|
|
||||||
OutputType outputType,
|
|
||||||
ConsoleColor foregroundColor,
|
|
||||||
ConsoleColor backgroundColor)
|
|
||||||
{
|
|
||||||
this.OutputText = outputText;
|
|
||||||
this.IncludeNewLine = includeNewLine;
|
|
||||||
this.OutputType = outputType;
|
|
||||||
this.ForegroundColor = foregroundColor;
|
|
||||||
this.BackgroundColor = backgroundColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Session
|
namespace Microsoft.SqlTools.ServiceLayer.SqlContext
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains details about the current host application (most
|
/// Contains details about the current host application (most
|
||||||
@@ -3,12 +3,11 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Session
|
namespace Microsoft.SqlTools.ServiceLayer.SqlContext
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides profile path resolution behavior relative to the name
|
/// Provides profile path resolution behavior relative to the name
|
||||||
@@ -5,8 +5,11 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Session
|
namespace Microsoft.SqlTools.ServiceLayer.SqlContext
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Context for SQL Tools
|
||||||
|
/// </summary>
|
||||||
public class SqlToolsContext
|
public class SqlToolsContext
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1,25 +1,29 @@
|
|||||||
//
|
using System.IO;
|
||||||
// Copyright (c) Microsoft. All rights reserved.
|
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
||||||
//
|
|
||||||
|
|
||||||
using System.IO;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Utility;
|
using Microsoft.SqlTools.EditorServices.Utility;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.Server
|
namespace Microsoft.SqlTools.ServiceLayer.SqlContext
|
||||||
{
|
{
|
||||||
public class LanguageServerSettings
|
/// <summary>
|
||||||
|
/// Class for serialization and deserialization of the settings the SQL Tools Service needs.
|
||||||
|
/// </summary>
|
||||||
|
public class SqlToolsSettings
|
||||||
{
|
{
|
||||||
public bool EnableProfileLoading { get; set; }
|
// TODO: Is this needed? I can't make sense of this comment.
|
||||||
|
// NOTE: This property is capitalized as 'SqlTools' because the
|
||||||
|
// mode name sent from the client is written as 'SqlTools' and
|
||||||
|
// JSON.net is using camelCasing.
|
||||||
|
//public ServiceHostSettings SqlTools { get; set; }
|
||||||
|
|
||||||
public ScriptAnalysisSettings ScriptAnalysis { get; set; }
|
public SqlToolsSettings()
|
||||||
|
|
||||||
public LanguageServerSettings()
|
|
||||||
{
|
{
|
||||||
this.ScriptAnalysis = new ScriptAnalysisSettings();
|
this.ScriptAnalysis = new ScriptAnalysisSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(LanguageServerSettings settings, string workspaceRootPath)
|
public bool EnableProfileLoading { get; set; }
|
||||||
|
|
||||||
|
public ScriptAnalysisSettings ScriptAnalysis { get; set; }
|
||||||
|
|
||||||
|
public void Update(SqlToolsSettings settings, string workspaceRootPath)
|
||||||
{
|
{
|
||||||
if (settings != null)
|
if (settings != null)
|
||||||
{
|
{
|
||||||
@@ -28,8 +32,10 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sub class for serialization and deserialization of script analysis settings
|
||||||
|
/// </summary>
|
||||||
public class ScriptAnalysisSettings
|
public class ScriptAnalysisSettings
|
||||||
{
|
{
|
||||||
public bool? Enable { get; set; }
|
public bool? Enable { get; set; }
|
||||||
@@ -77,14 +83,4 @@ namespace Microsoft.SqlTools.EditorServices.Protocol.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class LanguageServerSettingsWrapper
|
|
||||||
{
|
|
||||||
// NOTE: This property is capitalized as 'SqlTools' because the
|
|
||||||
// mode name sent from the client is written as 'SqlTools' and
|
|
||||||
// JSON.net is using camelCasing.
|
|
||||||
|
|
||||||
public LanguageServerSettings SqlTools { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices
|
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides details about a position in a file buffer. All
|
/// Provides details about a position in a file buffer. All
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices
|
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides details about a range between two positions in
|
/// Provides details about a range between two positions in
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
|
||||||
{
|
{
|
||||||
public class DidChangeConfigurationNotification<TConfig>
|
public class DidChangeConfigurationNotification<TConfig>
|
||||||
{
|
{
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices
|
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains details relating to a content change in an open file.
|
/// Contains details relating to a content change in an open file.
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices
|
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides details and operations for a buffer position in a
|
/// Provides details and operations for a buffer position in a
|
||||||
@@ -9,19 +9,13 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices
|
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains the details and contents of an open script file.
|
/// Contains the details and contents of an open script file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ScriptFile
|
public class ScriptFile
|
||||||
{
|
{
|
||||||
#region Private Fields
|
|
||||||
|
|
||||||
private Version SqlToolsVersion;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -120,18 +114,15 @@ namespace Microsoft.SqlTools.EditorServices
|
|||||||
/// <param name="filePath">The path at which the script file resides.</param>
|
/// <param name="filePath">The path at which the script file resides.</param>
|
||||||
/// <param name="clientFilePath">The path which the client uses to identify the file.</param>
|
/// <param name="clientFilePath">The path which the client uses to identify the file.</param>
|
||||||
/// <param name="textReader">The TextReader to use for reading the file's contents.</param>
|
/// <param name="textReader">The TextReader to use for reading the file's contents.</param>
|
||||||
/// <param name="SqlToolsVersion">The version of SqlTools for which the script is being parsed.</param>
|
|
||||||
public ScriptFile(
|
public ScriptFile(
|
||||||
string filePath,
|
string filePath,
|
||||||
string clientFilePath,
|
string clientFilePath,
|
||||||
TextReader textReader,
|
TextReader textReader)
|
||||||
Version SqlToolsVersion)
|
|
||||||
{
|
{
|
||||||
this.FilePath = filePath;
|
this.FilePath = filePath;
|
||||||
this.ClientFilePath = clientFilePath;
|
this.ClientFilePath = clientFilePath;
|
||||||
this.IsAnalysisEnabled = true;
|
this.IsAnalysisEnabled = true;
|
||||||
this.IsInMemory = Workspace.IsPathInMemory(filePath);
|
this.IsInMemory = Workspace.IsPathInMemory(filePath);
|
||||||
this.SqlToolsVersion = SqlToolsVersion;
|
|
||||||
|
|
||||||
this.SetFileContents(textReader.ReadToEnd());
|
this.SetFileContents(textReader.ReadToEnd());
|
||||||
}
|
}
|
||||||
@@ -142,17 +133,14 @@ namespace Microsoft.SqlTools.EditorServices
|
|||||||
/// <param name="filePath">The path at which the script file resides.</param>
|
/// <param name="filePath">The path at which the script file resides.</param>
|
||||||
/// <param name="clientFilePath">The path which the client uses to identify the file.</param>
|
/// <param name="clientFilePath">The path which the client uses to identify the file.</param>
|
||||||
/// <param name="initialBuffer">The initial contents of the script file.</param>
|
/// <param name="initialBuffer">The initial contents of the script file.</param>
|
||||||
/// <param name="SqlToolsVersion">The version of SqlTools for which the script is being parsed.</param>
|
|
||||||
public ScriptFile(
|
public ScriptFile(
|
||||||
string filePath,
|
string filePath,
|
||||||
string clientFilePath,
|
string clientFilePath,
|
||||||
string initialBuffer,
|
string initialBuffer)
|
||||||
Version SqlToolsVersion)
|
|
||||||
{
|
{
|
||||||
this.FilePath = filePath;
|
this.FilePath = filePath;
|
||||||
this.ClientFilePath = clientFilePath;
|
this.ClientFilePath = clientFilePath;
|
||||||
this.IsAnalysisEnabled = true;
|
this.IsAnalysisEnabled = true;
|
||||||
this.SqlToolsVersion = SqlToolsVersion;
|
|
||||||
|
|
||||||
this.SetFileContents(initialBuffer);
|
this.SetFileContents(initialBuffer);
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices
|
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines the message level of a script file marker.
|
/// Defines the message level of a script file marker.
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
//using System.Management.Automation.Language;
|
//using System.Management.Automation.Language;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices
|
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains details about a specific region of text in script file.
|
/// Contains details about a specific region of text in script file.
|
||||||
@@ -4,9 +4,9 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a base parameter class for identifying a text document.
|
/// Defines a base parameter class for identifying a text document.
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts
|
||||||
{
|
{
|
||||||
public enum SymbolKind
|
public enum SymbolKind
|
||||||
{
|
{
|
||||||
@@ -3,25 +3,25 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Utility;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.SqlTools.EditorServices.Utility;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices
|
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Manages a "workspace" of script files that are open for a particular
|
/// Manages a "workspace" of script files that are open for a particular
|
||||||
/// editing session. Also helps to navigate references between ScriptFiles.
|
/// editing session. Also helps to navigate references between ScriptFiles.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Workspace
|
public class Workspace : IDisposable
|
||||||
{
|
{
|
||||||
#region Private Fields
|
#region Private Fields
|
||||||
|
|
||||||
private Version SqlToolsVersion;
|
|
||||||
private Dictionary<string, ScriptFile> workspaceFiles = new Dictionary<string, ScriptFile>();
|
private Dictionary<string, ScriptFile> workspaceFiles = new Dictionary<string, ScriptFile>();
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -40,10 +40,8 @@ namespace Microsoft.SqlTools.EditorServices
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the Workspace class.
|
/// Creates a new instance of the Workspace class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="SqlToolsVersion">The version of SqlTools for which scripts will be parsed.</param>
|
public Workspace()
|
||||||
public Workspace(Version SqlToolsVersion)
|
|
||||||
{
|
{
|
||||||
this.SqlToolsVersion = SqlToolsVersion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -78,12 +76,7 @@ namespace Microsoft.SqlTools.EditorServices
|
|||||||
using (FileStream fileStream = new FileStream(resolvedFilePath, FileMode.Open, FileAccess.Read))
|
using (FileStream fileStream = new FileStream(resolvedFilePath, FileMode.Open, FileAccess.Read))
|
||||||
using (StreamReader streamReader = new StreamReader(fileStream, Encoding.UTF8))
|
using (StreamReader streamReader = new StreamReader(fileStream, Encoding.UTF8))
|
||||||
{
|
{
|
||||||
scriptFile =
|
scriptFile = new ScriptFile(resolvedFilePath, filePath,streamReader);
|
||||||
new ScriptFile(
|
|
||||||
resolvedFilePath,
|
|
||||||
filePath,
|
|
||||||
streamReader,
|
|
||||||
this.SqlToolsVersion);
|
|
||||||
|
|
||||||
this.workspaceFiles.Add(keyName, scriptFile);
|
this.workspaceFiles.Add(keyName, scriptFile);
|
||||||
}
|
}
|
||||||
@@ -169,12 +162,7 @@ namespace Microsoft.SqlTools.EditorServices
|
|||||||
ScriptFile scriptFile = null;
|
ScriptFile scriptFile = null;
|
||||||
if (!this.workspaceFiles.TryGetValue(keyName, out scriptFile))
|
if (!this.workspaceFiles.TryGetValue(keyName, out scriptFile))
|
||||||
{
|
{
|
||||||
scriptFile =
|
scriptFile = new ScriptFile(resolvedFilePath, filePath, initialBuffer);
|
||||||
new ScriptFile(
|
|
||||||
resolvedFilePath,
|
|
||||||
filePath,
|
|
||||||
initialBuffer,
|
|
||||||
this.SqlToolsVersion);
|
|
||||||
|
|
||||||
this.workspaceFiles.Add(keyName, scriptFile);
|
this.workspaceFiles.Add(keyName, scriptFile);
|
||||||
|
|
||||||
@@ -244,5 +232,17 @@ namespace Microsoft.SqlTools.EditorServices
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region IDisposable Implementation
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes of any Runspaces that were created for the
|
||||||
|
/// services used in this session.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
235
src/ServiceHost/WorkspaceServices/WorkspaceService.cs
Normal file
235
src/ServiceHost/WorkspaceServices/WorkspaceService.cs
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
//
|
||||||
|
// 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;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.EditorServices.Utility;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.WorkspaceServices
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class for handling requests/events that deal with the state of the workspace, including the
|
||||||
|
/// opening and closing of files, the changing of configuration, etc.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TConfig">
|
||||||
|
/// The type of the class used for serializing and deserializing the configuration. Must be the
|
||||||
|
/// actual type of the instance otherwise deserialization will be incomplete.
|
||||||
|
/// </typeparam>
|
||||||
|
public class WorkspaceService<TConfig> where TConfig : class, new()
|
||||||
|
{
|
||||||
|
|
||||||
|
#region Singleton Instance Implementation
|
||||||
|
|
||||||
|
private static readonly Lazy<WorkspaceService<TConfig>> instance = new Lazy<WorkspaceService<TConfig>>(() => new WorkspaceService<TConfig>());
|
||||||
|
|
||||||
|
public static WorkspaceService<TConfig> Instance
|
||||||
|
{
|
||||||
|
get { return instance.Value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default, parameterless constructor.
|
||||||
|
/// TODO: Figure out how to make this truely singleton even with dependency injection for tests
|
||||||
|
/// </summary>
|
||||||
|
public WorkspaceService()
|
||||||
|
{
|
||||||
|
ConfigChangeCallbacks = new List<ConfigChangeCallback>();
|
||||||
|
TextDocChangeCallbacks = new List<TextDocChangeCallback>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
public Workspace Workspace { get; private set; }
|
||||||
|
|
||||||
|
public TConfig CurrentSettings { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delegate for callbacks that occur when the configuration for the workspace changes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="newSettings">The settings that were just set</param>
|
||||||
|
/// <param name="oldSettings">The settings before they were changed</param>
|
||||||
|
/// <param name="eventContext">Context of the event that triggered the callback</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public delegate Task ConfigChangeCallback(TConfig newSettings, TConfig oldSettings, EventContext eventContext);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delegate for callbacks that occur when the current text document changes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="changedFiles">Array of files that changed</param>
|
||||||
|
/// <param name="eventContext">Context of the event raised for the changed files</param>
|
||||||
|
public delegate Task TextDocChangeCallback(ScriptFile[] changedFiles, EventContext eventContext);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of callbacks to call when the configuration of the workspace changes
|
||||||
|
/// </summary>
|
||||||
|
private List<ConfigChangeCallback> ConfigChangeCallbacks { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of callbacks to call when the current text document changes
|
||||||
|
/// </summary>
|
||||||
|
private List<TextDocChangeCallback> TextDocChangeCallbacks { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Public Methods
|
||||||
|
|
||||||
|
public void InitializeService(ServiceHost serviceHost)
|
||||||
|
{
|
||||||
|
// Create a workspace that will handle state for the session
|
||||||
|
Workspace = new Workspace();
|
||||||
|
CurrentSettings = new TConfig();
|
||||||
|
|
||||||
|
// Register the handlers for when changes to the workspae occur
|
||||||
|
serviceHost.SetEventHandler(DidChangeTextDocumentNotification.Type, HandleDidChangeTextDocumentNotification);
|
||||||
|
serviceHost.SetEventHandler(DidOpenTextDocumentNotification.Type, HandleDidOpenTextDocumentNotification);
|
||||||
|
serviceHost.SetEventHandler(DidCloseTextDocumentNotification.Type, HandleDidCloseTextDocumentNotification);
|
||||||
|
serviceHost.SetEventHandler(DidChangeConfigurationNotification<TConfig>.Type, HandleDidChangeConfigurationNotification);
|
||||||
|
|
||||||
|
// Register an initialization handler that sets the workspace path
|
||||||
|
serviceHost.RegisterInitializeTask(async (parameters, contect) =>
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "Initializing workspace service");
|
||||||
|
|
||||||
|
if (Workspace != null)
|
||||||
|
{
|
||||||
|
Workspace.WorkspacePath = parameters.RootPath;
|
||||||
|
}
|
||||||
|
await Task.FromResult(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register a shutdown request that disposes the workspace
|
||||||
|
serviceHost.RegisterShutdownTask(async (parameters, context) =>
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "Shutting down workspace service");
|
||||||
|
|
||||||
|
if (Workspace != null)
|
||||||
|
{
|
||||||
|
Workspace.Dispose();
|
||||||
|
Workspace = null;
|
||||||
|
}
|
||||||
|
await Task.FromResult(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a new task to be called when the configuration has been changed. Use this to
|
||||||
|
/// handle changing configuration and changing the current configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="task">Task to handle the request</param>
|
||||||
|
public void RegisterConfigChangeCallback(ConfigChangeCallback task)
|
||||||
|
{
|
||||||
|
ConfigChangeCallbacks.Add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a new task to be called when the text of a document changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="task">Delegate to call when the document changes</param>
|
||||||
|
public void RegisterTextDocChangeCallback(TextDocChangeCallback task)
|
||||||
|
{
|
||||||
|
TextDocChangeCallbacks.Add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Event Handlers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles text document change events
|
||||||
|
/// </summary>
|
||||||
|
protected Task HandleDidChangeTextDocumentNotification(
|
||||||
|
DidChangeTextDocumentParams textChangeParams,
|
||||||
|
EventContext eventContext)
|
||||||
|
{
|
||||||
|
StringBuilder msg = new StringBuilder();
|
||||||
|
msg.Append("HandleDidChangeTextDocumentNotification");
|
||||||
|
List<ScriptFile> changedFiles = new List<ScriptFile>();
|
||||||
|
|
||||||
|
// A text change notification can batch multiple change requests
|
||||||
|
foreach (var textChange in textChangeParams.ContentChanges)
|
||||||
|
{
|
||||||
|
string fileUri = textChangeParams.TextDocument.Uri;
|
||||||
|
msg.AppendLine(String.Format(" File: {0}", fileUri));
|
||||||
|
|
||||||
|
ScriptFile changedFile = Workspace.GetFile(fileUri);
|
||||||
|
|
||||||
|
changedFile.ApplyChange(
|
||||||
|
GetFileChangeDetails(
|
||||||
|
textChange.Range.Value,
|
||||||
|
textChange.Text));
|
||||||
|
|
||||||
|
changedFiles.Add(changedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Write(LogLevel.Verbose, msg.ToString());
|
||||||
|
|
||||||
|
var handlers = TextDocChangeCallbacks.Select(t => t(changedFiles.ToArray(), eventContext));
|
||||||
|
return Task.WhenAll(handlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Task HandleDidOpenTextDocumentNotification(
|
||||||
|
DidOpenTextDocumentNotification openParams,
|
||||||
|
EventContext eventContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleDidOpenTextDocumentNotification");
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Task HandleDidCloseTextDocumentNotification(
|
||||||
|
TextDocumentIdentifier closeParams,
|
||||||
|
EventContext eventContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleDidCloseTextDocumentNotification");
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the configuration change event
|
||||||
|
/// </summary>
|
||||||
|
protected async Task HandleDidChangeConfigurationNotification(
|
||||||
|
DidChangeConfigurationParams<TConfig> configChangeParams,
|
||||||
|
EventContext eventContext)
|
||||||
|
{
|
||||||
|
Logger.Write(LogLevel.Verbose, "HandleDidChangeConfigurationNotification");
|
||||||
|
|
||||||
|
// Propagate the changes to the event handlers
|
||||||
|
var configUpdateTasks = ConfigChangeCallbacks.Select(
|
||||||
|
t => t(configChangeParams.Settings, CurrentSettings, eventContext));
|
||||||
|
await Task.WhenAll(configUpdateTasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Private Helpers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Switch from 0-based offsets to 1 based offsets
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="changeRange"></param>
|
||||||
|
/// <param name="insertString"></param>
|
||||||
|
private static FileChange GetFileChangeDetails(Range changeRange, string insertString)
|
||||||
|
{
|
||||||
|
// The protocol's positions are zero-based so add 1 to all offsets
|
||||||
|
return new FileChange
|
||||||
|
{
|
||||||
|
InsertString = insertString,
|
||||||
|
Line = changeRange.Start.Line + 1,
|
||||||
|
Offset = changeRange.Start.Character + 1,
|
||||||
|
EndLine = changeRange.End.Line + 1,
|
||||||
|
EndOffset = changeRange.End.Character + 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.SqlTools.Test.Utility;
|
using Microsoft.SqlTools.Test.Utility;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.Test.Connection
|
namespace Microsoft.SqlTools.ServiceLayer.Test.Connection
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests for the ServiceHost Connection Service tests
|
/// Tests for the ServiceHost Connection Service tests
|
||||||
|
|||||||
@@ -3,14 +3,12 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices;
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||||
using Microsoft.SqlTools.EditorServices.Session;
|
using Microsoft.SqlTools.ServiceLayer.WorkspaceServices.Contracts;
|
||||||
using Microsoft.SqlTools.LanguageSupport;
|
|
||||||
using Microsoft.SqlTools.Test.Connection;
|
|
||||||
using Microsoft.SqlTools.Test.Utility;
|
using Microsoft.SqlTools.Test.Utility;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.Test.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.Test.LanguageServer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests for the ServiceHost Language Service tests
|
/// Tests for the ServiceHost Language Service tests
|
||||||
|
|||||||
@@ -3,15 +3,16 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Serializers;
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
using HostingMessage = Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts.Message;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Test.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Test.Message
|
||||||
{
|
{
|
||||||
public class MessageReaderWriterTests
|
public class MessageReaderWriterTests
|
||||||
{
|
{
|
||||||
@@ -38,7 +39,7 @@ namespace Microsoft.SqlTools.EditorServices.Test.Protocol.MessageProtocol
|
|||||||
|
|
||||||
// Write the message and then roll back the stream to be read
|
// Write the message and then roll back the stream to be read
|
||||||
// TODO: This will need to be redone!
|
// TODO: This will need to be redone!
|
||||||
await messageWriter.WriteMessage(Message.Event("testEvent", null));
|
await messageWriter.WriteMessage(HostingMessage.Event("testEvent", null));
|
||||||
outputStream.Seek(0, SeekOrigin.Begin);
|
outputStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
string expectedHeaderString =
|
string expectedHeaderString =
|
||||||
@@ -82,7 +83,7 @@ namespace Microsoft.SqlTools.EditorServices.Test.Protocol.MessageProtocol
|
|||||||
inputStream.Flush();
|
inputStream.Flush();
|
||||||
inputStream.Seek(0, SeekOrigin.Begin);
|
inputStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
Message messageResult = messageReader.ReadMessage().Result;
|
HostingMessage messageResult = messageReader.ReadMessage().Result;
|
||||||
Assert.Equal("testEvent", messageResult.Method);
|
Assert.Equal("testEvent", messageResult.Method);
|
||||||
|
|
||||||
inputStream.Dispose();
|
inputStream.Dispose();
|
||||||
@@ -117,7 +118,7 @@ namespace Microsoft.SqlTools.EditorServices.Test.Protocol.MessageProtocol
|
|||||||
// Read the written messages from the stream
|
// Read the written messages from the stream
|
||||||
for (int i = 0; i < overflowMessageCount; i++)
|
for (int i = 0; i < overflowMessageCount; i++)
|
||||||
{
|
{
|
||||||
Message messageResult = messageReader.ReadMessage().Result;
|
HostingMessage messageResult = messageReader.ReadMessage().Result;
|
||||||
Assert.Equal("testEvent", messageResult.Method);
|
Assert.Equal("testEvent", messageResult.Method);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +146,7 @@ namespace Microsoft.SqlTools.EditorServices.Test.Protocol.MessageProtocol
|
|||||||
inputStream.Flush();
|
inputStream.Flush();
|
||||||
inputStream.Seek(0, SeekOrigin.Begin);
|
inputStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
Message messageResult = messageReader.ReadMessage().Result;
|
HostingMessage messageResult = messageReader.ReadMessage().Result;
|
||||||
Assert.Equal("testEvent", messageResult.Method);
|
Assert.Equal("testEvent", messageResult.Method);
|
||||||
|
|
||||||
inputStream.Dispose();
|
inputStream.Dispose();
|
||||||
|
|||||||
@@ -3,19 +3,16 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Test.Protocol.MessageProtocol
|
namespace Microsoft.SqlTools.ServiceLayer.Test.Message
|
||||||
{
|
{
|
||||||
#region Request Types
|
#region Request Types
|
||||||
|
|
||||||
internal class TestRequest
|
internal class TestRequest
|
||||||
{
|
{
|
||||||
public Task ProcessMessage(
|
public Task ProcessMessage(MessageWriter messageWriter)
|
||||||
EditorSession editorSession,
|
|
||||||
MessageWriter messageWriter)
|
|
||||||
{
|
{
|
||||||
return Task.FromResult(false);
|
return Task.FromResult(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<ProjectGuid>2d771d16-9d85-4053-9f79-e2034737deef</ProjectGuid>
|
<ProjectGuid>2d771d16-9d85-4053-9f79-e2034737deef</ProjectGuid>
|
||||||
<RootNamespace>Microsoft.SqlTools.EditorServices.Test.Protocol</RootNamespace>
|
<RootNamespace>Microsoft.SqlTools.ServiceLayer.Test</RootNamespace>
|
||||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'==''">.\obj</BaseIntermediateOutputPath>
|
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'==''">.\obj</BaseIntermediateOutputPath>
|
||||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
@@ -15,5 +15,8 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
</Project>
|
</Project>
|
||||||
@@ -3,12 +3,12 @@
|
|||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol;
|
using Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Serializers;
|
||||||
using Microsoft.SqlTools.EditorServices.Protocol.MessageProtocol.Serializers;
|
using HostingMessage = Microsoft.SqlTools.ServiceLayer.Hosting.Protocol.Contracts.Message;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.EditorServices.Test.Protocol.LanguageServer
|
namespace Microsoft.SqlTools.ServiceLayer.Test.ServiceHost
|
||||||
{
|
{
|
||||||
public class TestMessageContents
|
public class TestMessageContents
|
||||||
{
|
{
|
||||||
@@ -44,7 +44,7 @@ namespace Microsoft.SqlTools.EditorServices.Test.Protocol.LanguageServer
|
|||||||
{
|
{
|
||||||
var messageObj =
|
var messageObj =
|
||||||
this.messageSerializer.SerializeMessage(
|
this.messageSerializer.SerializeMessage(
|
||||||
Message.Request(
|
HostingMessage.Request(
|
||||||
MessageId,
|
MessageId,
|
||||||
MethodName,
|
MethodName,
|
||||||
MessageContent));
|
MessageContent));
|
||||||
@@ -61,7 +61,7 @@ namespace Microsoft.SqlTools.EditorServices.Test.Protocol.LanguageServer
|
|||||||
{
|
{
|
||||||
var messageObj =
|
var messageObj =
|
||||||
this.messageSerializer.SerializeMessage(
|
this.messageSerializer.SerializeMessage(
|
||||||
Message.Event(
|
HostingMessage.Event(
|
||||||
MethodName,
|
MethodName,
|
||||||
MessageContent));
|
MessageContent));
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ namespace Microsoft.SqlTools.EditorServices.Test.Protocol.LanguageServer
|
|||||||
{
|
{
|
||||||
var messageObj =
|
var messageObj =
|
||||||
this.messageSerializer.SerializeMessage(
|
this.messageSerializer.SerializeMessage(
|
||||||
Message.Response(
|
HostingMessage.Response(
|
||||||
MessageId,
|
MessageId,
|
||||||
null,
|
null,
|
||||||
MessageContent));
|
MessageContent));
|
||||||
@@ -92,7 +92,7 @@ namespace Microsoft.SqlTools.EditorServices.Test.Protocol.LanguageServer
|
|||||||
{
|
{
|
||||||
var messageObj =
|
var messageObj =
|
||||||
this.messageSerializer.SerializeMessage(
|
this.messageSerializer.SerializeMessage(
|
||||||
Message.ResponseError(
|
HostingMessage.ResponseError(
|
||||||
MessageId,
|
MessageId,
|
||||||
null,
|
null,
|
||||||
MessageContent));
|
MessageContent));
|
||||||
@@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.SqlTools.EditorServices.Connection;
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
using Microsoft.SqlTools.EditorServices.Session;
|
using Microsoft.SqlTools.ServiceLayer.LanguageServices;
|
||||||
using Microsoft.SqlTools.LanguageSupport;
|
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.Test.Utility
|
namespace Microsoft.SqlTools.Test.Utility
|
||||||
@@ -53,7 +53,7 @@ namespace Microsoft.SqlTools.Test.Utility
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static LanguageService GetTestLanguageService()
|
public static LanguageService GetTestLanguageService()
|
||||||
{
|
{
|
||||||
return new LanguageService(new SqlToolsContext(null, null));
|
return new LanguageService();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user