From 79ee230edd6574b72b392282035b6dd403c01e93 Mon Sep 17 00:00:00 2001 From: Tien Nguyen Date: Thu, 10 Sep 2015 11:38:41 -0700 Subject: [PATCH 1/5] Create the menu group. --- .../InteractiveWindow/InteractiveWindow.vsct | 3 +- .../Communication/DebuggerConnection.cs | 550 +++++++++--------- Nodejs/Product/Nodejs/NodejsTools.vsct | 15 +- Nodejs/Product/Nodejs/PkgCmdId.cs | 1 + .../Nodejs/Project/NodejsProjectNode.cs | 6 + 5 files changed, 298 insertions(+), 277 deletions(-) diff --git a/Nodejs/Product/InteractiveWindow/InteractiveWindow.vsct b/Nodejs/Product/InteractiveWindow/InteractiveWindow.vsct index 537db3672..55873f187 100644 --- a/Nodejs/Product/InteractiveWindow/InteractiveWindow.vsct +++ b/Nodejs/Product/InteractiveWindow/InteractiveWindow.vsct @@ -299,12 +299,13 @@ - + + diff --git a/Nodejs/Product/Nodejs/Debugger/Communication/DebuggerConnection.cs b/Nodejs/Product/Nodejs/Debugger/Communication/DebuggerConnection.cs index 1130e425e..d2b61f36e 100644 --- a/Nodejs/Product/Nodejs/Debugger/Communication/DebuggerConnection.cs +++ b/Nodejs/Product/Nodejs/Debugger/Communication/DebuggerConnection.cs @@ -1,124 +1,124 @@ -//*********************************************************// -// Copyright (c) Microsoft. All rights reserved. -// -// Apache 2.0 License -// -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -// implied. See the License for the specific language governing -// permissions and limitations under the License. -// -//*********************************************************// - -using System; -using System.Diagnostics; -using System.IO; -using System.Net.Sockets; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Microsoft.NodejsTools.Logging; -using Microsoft.VisualStudioTools; -using Microsoft.VisualStudioTools.Project; -using Newtonsoft.Json; - -namespace Microsoft.NodejsTools.Debugger.Communication { - sealed class DebuggerConnection : IDebuggerConnection { - private static readonly Encoding _encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); - private static readonly Regex _contentLengthFieldRegex = new Regex(@"Content-Length: (\d+)", RegexOptions.Compiled); - private static readonly Regex _nodeVersionFieldRegex = new Regex(@"Embedding-Host: node v([0-9.]+)", RegexOptions.Compiled); - - private readonly AsyncProducerConsumerCollection _packetsToSend = new AsyncProducerConsumerCollection(); - private readonly INetworkClientFactory _networkClientFactory; - private INetworkClient _networkClient; - private readonly object _networkClientLock = new object(); - private volatile Version _nodeVersion; - - public DebuggerConnection(INetworkClientFactory networkClientFactory) { - Utilities.ArgumentNotNull("networkClientFactory", networkClientFactory); - - _networkClientFactory = networkClientFactory; - } - - public void Dispose() { - Close(); - } - - /// - /// Close connection. - /// - public void Close() { - lock (_networkClientLock) { - if (_networkClient != null) { - _networkClient.Dispose(); - _networkClient = null; - } - } - } - - /// - /// Send a message. - /// - /// Message. - public void SendMessage(string message) { - Utilities.ArgumentNotNullOrEmpty("message", message); - - if (!Connected) { - return; - } - - LiveLogger.WriteLine("Request: " + message, typeof(DebuggerConnection)); - - var messageBody = _encoding.GetBytes(message); - var messageHeader = _encoding.GetBytes(string.Format("Content-Length: {0}\r\n\r\n", messageBody.Length)); - _packetsToSend.Add(messageHeader); - _packetsToSend.Add(messageBody); - } - - /// - /// Fired when received inbound message. - /// - public event EventHandler OutputMessage; - - /// - /// Fired when connection was closed. - /// - public event EventHandler ConnectionClosed; - - /// - /// Gets a value indicating whether connection established. - /// - public bool Connected { - get { - lock (_networkClientLock) { - return _networkClient != null && _networkClient.Connected; - } - } - } - - /// - /// Gets a Node.js version, or null if it was not supplied by the debuggee. - /// - public Version NodeVersion { - get { return _nodeVersion; } - } - - /// - /// Connect to specified debugger endpoint. - /// - /// URI identifying the endpoint to connect to. - public void Connect(Uri uri) { - Utilities.ArgumentNotNull("uri", uri); - LiveLogger.WriteLine("Debugger connecting to URI: {0}", uri); - - Close(); - lock (_networkClientLock) { - int connection_attempts = 0; - const int MAX_ATTEMPTS = 5; +//*********************************************************// +// Copyright (c) Microsoft. All rights reserved. +// +// Apache 2.0 License +// +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//*********************************************************// + +using System; +using System.Diagnostics; +using System.IO; +using System.Net.Sockets; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.NodejsTools.Logging; +using Microsoft.VisualStudioTools; +using Microsoft.VisualStudioTools.Project; +using Newtonsoft.Json; + +namespace Microsoft.NodejsTools.Debugger.Communication { + sealed class DebuggerConnection : IDebuggerConnection { + private static readonly Encoding _encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + private static readonly Regex _contentLengthFieldRegex = new Regex(@"Content-Length: (\d+)", RegexOptions.Compiled); + private static readonly Regex _nodeVersionFieldRegex = new Regex(@"Embedding-Host: node v([0-9.]+)", RegexOptions.Compiled); + + private readonly AsyncProducerConsumerCollection _packetsToSend = new AsyncProducerConsumerCollection(); + private readonly INetworkClientFactory _networkClientFactory; + private INetworkClient _networkClient; + private readonly object _networkClientLock = new object(); + private volatile Version _nodeVersion; + + public DebuggerConnection(INetworkClientFactory networkClientFactory) { + Utilities.ArgumentNotNull("networkClientFactory", networkClientFactory); + + _networkClientFactory = networkClientFactory; + } + + public void Dispose() { + Close(); + } + + /// + /// Close connection. + /// + public void Close() { + lock (_networkClientLock) { + if (_networkClient != null) { + _networkClient.Dispose(); + _networkClient = null; + } + } + } + + /// + /// Send a message. + /// + /// Message. + public void SendMessage(string message) { + Utilities.ArgumentNotNullOrEmpty("message", message); + + if (!Connected) { + return; + } + + LiveLogger.WriteLine("Request: " + message, typeof(DebuggerConnection)); + + var messageBody = _encoding.GetBytes(message); + var messageHeader = _encoding.GetBytes(string.Format("Content-Length: {0}\r\n\r\n", messageBody.Length)); + _packetsToSend.Add(messageHeader); + _packetsToSend.Add(messageBody); + } + + /// + /// Fired when received inbound message. + /// + public event EventHandler OutputMessage; + + /// + /// Fired when connection was closed. + /// + public event EventHandler ConnectionClosed; + + /// + /// Gets a value indicating whether connection established. + /// + public bool Connected { + get { + lock (_networkClientLock) { + return _networkClient != null && _networkClient.Connected; + } + } + } + + /// + /// Gets a Node.js version, or null if it was not supplied by the debuggee. + /// + public Version NodeVersion { + get { return _nodeVersion; } + } + + /// + /// Connect to specified debugger endpoint. + /// + /// URI identifying the endpoint to connect to. + public void Connect(Uri uri) { + Utilities.ArgumentNotNull("uri", uri); + LiveLogger.WriteLine("Debugger connecting to URI: {0}", uri); + + Close(); + lock (_networkClientLock) { + int connection_attempts = 0; + const int MAX_ATTEMPTS = 5; while (true) { connection_attempts++; try { @@ -155,158 +155,158 @@ public void Connect(Uri uri) { System.Threading.Thread.Sleep(200); } } - } - } - - Task.Factory.StartNew(ReceiveAndDispatchMessagesWorker); - Task.Factory.StartNew(SendPacketsWorker); - } - - /// - /// Sends packets queued by . - /// - private async void SendPacketsWorker() { - INetworkClient networkClient; - lock (_networkClientLock) { - networkClient = _networkClient; - } - if (networkClient == null) { - return; - } - - try { - var stream = networkClient.GetStream(); - while (Connected) { - byte[] packet = await _packetsToSend.TakeAsync().ConfigureAwait(false); - await stream.WriteAsync(packet, 0, packet.Length).ConfigureAwait(false); - await stream.FlushAsync().ConfigureAwait(false); - } - } catch (SocketException) { - } catch (ObjectDisposedException) { - } catch (IOException) { - } catch (Exception e) { - LiveLogger.WriteLine(string.Format("Failed to write message {0}.", e), typeof(DebuggerConnection)); - throw; - } - } - - /// - /// Receives messages from debugger, parses them to extract the body, and dispatches them to listeners. - /// - private async void ReceiveAndDispatchMessagesWorker() { - LiveLogger.WriteLine("Established connection.", typeof(DebuggerConnection)); - - INetworkClient networkClient; - lock (_networkClientLock) { - networkClient = _networkClient; - } - if (networkClient == null) { - return; - } - - try { - var stream = networkClient.GetStream(); - - // Use a single read buffer and a single StringBuilder (periodically cleared) across loop iterations, - // to avoid costly repeated allocations. - var buffer = new byte[0x1000]; - var sb = new StringBuilder(); - - // Read and process incoming messages until disconnected. - while (true) { - // Read the header of this message. - int contentLength = 0; - while (true) { - // Read a single header field. - string field; - sb.Clear(); - while (true) { - int bytesRead = await stream.ReadAsync(buffer, 0, 1).ConfigureAwait(false); - if (bytesRead < 1) { - // End of stream - we are disconnected from debuggee. - throw new EndOfStreamException(); - } - - // All fields that we care about are ASCII, and for all the other fields we only need to recognize - // the trailing \r\n, so there's no need to do proper decoding here. - sb.Append((char)buffer[0]); - - // "\r\n" terminates the field. - if (sb.Length >= 2 && sb[sb.Length - 2] == '\r' && sb[sb.Length - 1] == '\n') { - field = sb.ToString(0, sb.Length - 2); - break; - } - } - - // Blank line terminates the header. - if (string.IsNullOrEmpty(field)) { - break; - } - - // Otherwise, it's an actual field. Parse it if it's something we care about. - - // Content-Length - var match = _contentLengthFieldRegex.Match(field); - if (match.Success) { - int.TryParse(match.Groups[1].Value, out contentLength); - continue; - } - - // Embedding-Host, which contains the Node.js version number. Only try parsing that if we don't know the version yet - - // it normally comes in the very first packet, so this saves time trying to parse all the consequent ones. - if (NodeVersion == null) { - match = _nodeVersionFieldRegex.Match(field); - if (match.Success) { - Version nodeVersion; - Version.TryParse(match.Groups[1].Value, out nodeVersion); - _nodeVersion = nodeVersion; - } - } - } - - if (contentLength == 0) { - continue; - } - - // Read the body of this message. - - // If our preallocated buffer is large enough, use it - this should be true for vast majority of messages. - // If not, allocate a buffer that is large enough and use that, then throw it away - don't replace the original - // buffer with it, so that we don't hold onto a huge chunk of memory for the rest of the debugging session just - // because of a single long message. - var bodyBuffer = buffer.Length >= contentLength ? buffer : new byte[contentLength]; - - for (int i = 0; i < contentLength; ) { - i += await stream.ReadAsync(bodyBuffer, i, contentLength - i).ConfigureAwait(false); - } - - string message = _encoding.GetString(bodyBuffer, 0, contentLength); - LiveLogger.WriteLine("Response: " + message, typeof(DebuggerConnection)); - - // Notify subscribers. - var outputMessage = OutputMessage; - if (outputMessage != null) { - outputMessage(this, new MessageEventArgs(message)); - } - } - } catch (SocketException) { - } catch (IOException) { - } catch (ObjectDisposedException) { - } catch (DecoderFallbackException ex) { - LiveLogger.WriteLine(string.Format("Error decoding response body: {0}", ex), typeof(DebuggerConnection)); - } catch (JsonReaderException ex) { - LiveLogger.WriteLine(string.Format("Error parsing JSON response: {0}", ex), typeof(DebuggerConnection)); - } catch (Exception ex) { - LiveLogger.WriteLine(string.Format("Message processing failed: {0}", ex), typeof(DebuggerConnection)); - throw; - } finally { - LiveLogger.WriteLine("Connection was closed.", typeof(DebuggerConnection)); - - var connectionClosed = ConnectionClosed; - if (connectionClosed != null) { - connectionClosed(this, EventArgs.Empty); - } - } - } - } + } + } + + Task.Factory.StartNew(ReceiveAndDispatchMessagesWorker); + Task.Factory.StartNew(SendPacketsWorker); + } + + /// + /// Sends packets queued by . + /// + private async void SendPacketsWorker() { + INetworkClient networkClient; + lock (_networkClientLock) { + networkClient = _networkClient; + } + if (networkClient == null) { + return; + } + + try { + var stream = networkClient.GetStream(); + while (Connected) { + byte[] packet = await _packetsToSend.TakeAsync().ConfigureAwait(false); + await stream.WriteAsync(packet, 0, packet.Length).ConfigureAwait(false); + await stream.FlushAsync().ConfigureAwait(false); + } + } catch (SocketException) { + } catch (ObjectDisposedException) { + } catch (IOException) { + } catch (Exception e) { + LiveLogger.WriteLine(string.Format("Failed to write message {0}.", e), typeof(DebuggerConnection)); + throw; + } + } + + /// + /// Receives messages from debugger, parses them to extract the body, and dispatches them to listeners. + /// + private async void ReceiveAndDispatchMessagesWorker() { + LiveLogger.WriteLine("Established connection.", typeof(DebuggerConnection)); + + INetworkClient networkClient; + lock (_networkClientLock) { + networkClient = _networkClient; + } + if (networkClient == null) { + return; + } + + try { + var stream = networkClient.GetStream(); + + // Use a single read buffer and a single StringBuilder (periodically cleared) across loop iterations, + // to avoid costly repeated allocations. + var buffer = new byte[0x1000]; + var sb = new StringBuilder(); + + // Read and process incoming messages until disconnected. + while (true) { + // Read the header of this message. + int contentLength = 0; + while (true) { + // Read a single header field. + string field; + sb.Clear(); + while (true) { + int bytesRead = await stream.ReadAsync(buffer, 0, 1).ConfigureAwait(false); + if (bytesRead < 1) { + // End of stream - we are disconnected from debuggee. + throw new EndOfStreamException(); + } + + // All fields that we care about are ASCII, and for all the other fields we only need to recognize + // the trailing \r\n, so there's no need to do proper decoding here. + sb.Append((char)buffer[0]); + + // "\r\n" terminates the field. + if (sb.Length >= 2 && sb[sb.Length - 2] == '\r' && sb[sb.Length - 1] == '\n') { + field = sb.ToString(0, sb.Length - 2); + break; + } + } + + // Blank line terminates the header. + if (string.IsNullOrEmpty(field)) { + break; + } + + // Otherwise, it's an actual field. Parse it if it's something we care about. + + // Content-Length + var match = _contentLengthFieldRegex.Match(field); + if (match.Success) { + int.TryParse(match.Groups[1].Value, out contentLength); + continue; + } + + // Embedding-Host, which contains the Node.js version number. Only try parsing that if we don't know the version yet - + // it normally comes in the very first packet, so this saves time trying to parse all the consequent ones. + if (NodeVersion == null) { + match = _nodeVersionFieldRegex.Match(field); + if (match.Success) { + Version nodeVersion; + Version.TryParse(match.Groups[1].Value, out nodeVersion); + _nodeVersion = nodeVersion; + } + } + } + + if (contentLength == 0) { + continue; + } + + // Read the body of this message. + + // If our preallocated buffer is large enough, use it - this should be true for vast majority of messages. + // If not, allocate a buffer that is large enough and use that, then throw it away - don't replace the original + // buffer with it, so that we don't hold onto a huge chunk of memory for the rest of the debugging session just + // because of a single long message. + var bodyBuffer = buffer.Length >= contentLength ? buffer : new byte[contentLength]; + + for (int i = 0; i < contentLength; ) { + i += await stream.ReadAsync(bodyBuffer, i, contentLength - i).ConfigureAwait(false); + } + + string message = _encoding.GetString(bodyBuffer, 0, contentLength); + LiveLogger.WriteLine("Response: " + message, typeof(DebuggerConnection)); + + // Notify subscribers. + var outputMessage = OutputMessage; + if (outputMessage != null) { + outputMessage(this, new MessageEventArgs(message)); + } + } + } catch (SocketException) { + } catch (IOException) { + } catch (ObjectDisposedException) { + } catch (DecoderFallbackException ex) { + LiveLogger.WriteLine(string.Format("Error decoding response body: {0}", ex), typeof(DebuggerConnection)); + } catch (JsonReaderException ex) { + LiveLogger.WriteLine(string.Format("Error parsing JSON response: {0}", ex), typeof(DebuggerConnection)); + } catch (Exception ex) { + LiveLogger.WriteLine(string.Format("Message processing failed: {0}", ex), typeof(DebuggerConnection)); + throw; + } finally { + LiveLogger.WriteLine("Connection was closed.", typeof(DebuggerConnection)); + + var connectionClosed = ConnectionClosed; + if (connectionClosed != null) { + connectionClosed(this, EventArgs.Empty); + } + } + } + } } \ No newline at end of file diff --git a/Nodejs/Product/Nodejs/NodejsTools.vsct b/Nodejs/Product/Nodejs/NodejsTools.vsct index a884b4137..903120a62 100644 --- a/Nodejs/Product/Nodejs/NodejsTools.vsct +++ b/Nodejs/Product/Nodejs/NodejsTools.vsct @@ -51,6 +51,10 @@ + + + + @@ -87,6 +91,13 @@ DynamicVisibility If you do not want an image next to your command, remove the Icon node or set it to --> + + + + + + + + @@ -376,6 +397,9 @@ + + + diff --git a/Nodejs/Product/Nodejs/PkgCmdId.cs b/Nodejs/Product/Nodejs/PkgCmdId.cs index 73f1f51dd..23c05d3c8 100644 --- a/Nodejs/Product/Nodejs/PkgCmdId.cs +++ b/Nodejs/Product/Nodejs/PkgCmdId.cs @@ -37,6 +37,9 @@ class PkgCmdId { public const int cmdidSetAsContent = 0x209; public const int cmdidSetAsCompile = 0x210; public const int cmdidAddNewJavaScriptFileCommand = 0x211; + public const int cmdidAddNewTypeScriptFileCommand = 0x212; + public const int cmdidAddNewHTMLFileCommand = 0x213; + public const int cmdidAddNewCSSFileCommand = 0x214; public const int cmdidNpmManageModules = 0x300; public const int cmdidNpmInstallModules = 0x301; diff --git a/Nodejs/Product/Nodejs/Project/NewFileNameForm.Designer.cs b/Nodejs/Product/Nodejs/Project/NewFileMenuGroup/NewFileNameForm.Designer.cs similarity index 95% rename from Nodejs/Product/Nodejs/Project/NewFileNameForm.Designer.cs rename to Nodejs/Product/Nodejs/Project/NewFileMenuGroup/NewFileNameForm.Designer.cs index d2cac3bd0..d524c763f 100644 --- a/Nodejs/Product/Nodejs/Project/NewFileNameForm.Designer.cs +++ b/Nodejs/Product/Nodejs/Project/NewFileMenuGroup/NewFileNameForm.Designer.cs @@ -48,6 +48,7 @@ private void InitializeComponent() this.textBox.Name = "textBox"; this.textBox.Size = new System.Drawing.Size(306, 20); this.textBox.TabIndex = 1; + this.textBox.TextChanged += this.TextBox_TextChanged; // // cancelButton // @@ -87,7 +88,6 @@ private void InitializeComponent() this.ShowIcon = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Specify Name for Item"; - this.Load += new System.EventHandler(this.NewFileNameForm_Load); this.ResumeLayout(false); this.PerformLayout(); diff --git a/Nodejs/Product/Nodejs/Project/NewFileNameForm.cs b/Nodejs/Product/Nodejs/Project/NewFileMenuGroup/NewFileNameForm.cs similarity index 71% rename from Nodejs/Product/Nodejs/Project/NewFileNameForm.cs rename to Nodejs/Product/Nodejs/Project/NewFileMenuGroup/NewFileNameForm.cs index 64bf80d30..7bcd2515d 100644 --- a/Nodejs/Product/Nodejs/Project/NewFileNameForm.cs +++ b/Nodejs/Product/Nodejs/Project/NewFileMenuGroup/NewFileNameForm.cs @@ -1,17 +1,17 @@ -//*********************************************************// -// Copyright (c) Microsoft. All rights reserved. -// -// Apache 2.0 License -// -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -// implied. See the License for the specific language governing -// permissions and limitations under the License. -// +//*********************************************************// +// Copyright (c) Microsoft. All rights reserved. +// +// Apache 2.0 License +// +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. +// //*********************************************************// using System; @@ -21,14 +21,11 @@ namespace Microsoft.NodejsTools.Project { public partial class NewFileNameForm : Form { - public NewFileNameForm() + public NewFileNameForm(string initialFileName) { InitializeComponent(); - } - - private void NewFileNameForm_Load(object sender, EventArgs e) - { + TextBox.Text = initialFileName; } public TextBox TextBox @@ -37,5 +34,15 @@ public TextBox TextBox return textBox; } } + + private void TextBox_TextChanged(object sender, EventArgs e) + { + if (TextBox.Text.Trim().Length == 0) { + okButton.Enabled = false; + } + else { + okButton.Enabled = true; + } + } } } diff --git a/Nodejs/Product/Nodejs/Project/NewFileMenuGroup/NewFileUtilities.cs b/Nodejs/Product/Nodejs/Project/NewFileMenuGroup/NewFileUtilities.cs new file mode 100644 index 000000000..26ff13289 --- /dev/null +++ b/Nodejs/Product/Nodejs/Project/NewFileMenuGroup/NewFileUtilities.cs @@ -0,0 +1,104 @@ +using System; +using System.Diagnostics; +using Microsoft.VisualStudio.Shell.Interop; + + +namespace Microsoft.NodejsTools.Project.NewFileMenuGroup +{ + internal static class NewFileUtilities + { + public static string GetTemplateFile(string fileType) + { + string templateFileName = null; + + switch (fileType) { + case NodejsConstants.JavaScript: + templateFileName = "EmptyJs.js"; + break; + case NodejsConstants.TypeScript: + templateFileName = "EmptyTs.ts"; + break; + case NodejsConstants.HTML: + templateFileName = "EmptyHTML.html"; + break; + case NodejsConstants.CSS: + templateFileName = "EmptyCSS.css"; + break; + } + + if (templateFileName == null) { + Debug.Fail(String.Format("Invalid file type: {0}", fileType)); + } + + return NodejsToolsInstallPath.GetFile("FileTemplates\\NewItem\\" + templateFileName); + } + + private static string GetInitialName(string fileType) + { + string name = null; + + switch (fileType) { + case NodejsConstants.JavaScript: + name = "JavaScript.js"; + break; + case NodejsConstants.TypeScript: + name = "TypeScript.ts"; + break; + case NodejsConstants.HTML: + name = "HTML.html"; + break; + case NodejsConstants.CSS: + name = "CSS.css"; + break; + } + + if (name == null) { + Debug.Fail(String.Format("Invalid file type: {0}", fileType)); + } + + return name; + } + + private static void CreateNewFile(NodejsProjectNode projectNode, uint containerId, string fileType) + { + using (var dialog = new NewFileNameForm(GetInitialName(fileType))) { + if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + string itemName = dialog.TextBox.Text; + + VSADDRESULT[] pResult = new VSADDRESULT[1]; + projectNode.AddItem( + containerId, // Identifier of the container folder. + VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE, // Indicate that we want to create this new file by cloning a template file. + itemName, + 1, // Number of templates in the next parameter. Must be 1 if using VSADDITEMOP_CLONEFILE. + new string[] { GetTemplateFile(fileType) }, // Array contains the template file path. + IntPtr.Zero, // Handle to the Add Item dialog box. Must be Zero if using VSADDITEMOP_CLONEFILE. + pResult + ); + + // TODO: Do we need to check if result[0] = VSADDRESULT.ADDRESULT_Success here? + } + } + } + + internal static void CreateNewJavaScriptFile(NodejsProjectNode projectNode, uint containerId) + { + CreateNewFile(projectNode, containerId, NodejsConstants.JavaScript); + } + + internal static void CreateNewTypeScriptFile(NodejsProjectNode projectNode, uint containerId) + { + CreateNewFile(projectNode, containerId, NodejsConstants.TypeScript); + } + + internal static void CreateNewHTMLFile(NodejsProjectNode projectNode, uint containerId) + { + CreateNewFile(projectNode, containerId, NodejsConstants.HTML); + } + + internal static void CreateNewCSSFile(NodejsProjectNode projectNode, uint containerId) + { + CreateNewFile(projectNode, containerId, NodejsConstants.CSS); + } + } +} diff --git a/Nodejs/Product/Nodejs/Project/NodejsProjectNode.cs b/Nodejs/Product/Nodejs/Project/NodejsProjectNode.cs index 2c3c3f122..6036ed159 100644 --- a/Nodejs/Product/Nodejs/Project/NodejsProjectNode.cs +++ b/Nodejs/Product/Nodejs/Project/NodejsProjectNode.cs @@ -1028,6 +1028,9 @@ protected override QueryStatusResult QueryStatusSelectionOnNodes(IList Date: Sun, 13 Sep 2015 18:39:12 -0700 Subject: [PATCH 5/5] The only change in these files is line ending format. --- .../Nodejs/Commands/DiagnosticsForm.cs | 82 +-- .../Communication/DebuggerConnection.cs | 550 +++++++++--------- 2 files changed, 316 insertions(+), 316 deletions(-) diff --git a/Nodejs/Product/Nodejs/Commands/DiagnosticsForm.cs b/Nodejs/Product/Nodejs/Commands/DiagnosticsForm.cs index 140a3c8c6..1f8d44ee8 100644 --- a/Nodejs/Product/Nodejs/Commands/DiagnosticsForm.cs +++ b/Nodejs/Product/Nodejs/Commands/DiagnosticsForm.cs @@ -1,46 +1,46 @@ -//*********************************************************// -// Copyright (c) Microsoft. All rights reserved. -// -// Apache 2.0 License -// -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -// implied. See the License for the specific language governing -// permissions and limitations under the License. -// -//*********************************************************// - -using System; -using System.Windows.Forms; - -namespace Microsoft.NodejsTools.Commands { - public partial class DiagnosticsForm : Form { - public DiagnosticsForm(string content) { - InitializeComponent(); - _textBox.Text = content; - } - - public TextBox TextBox { - get { - return _textBox; - } - } - - private void _ok_Click(object sender, EventArgs e) { - Close(); - } - - private void _copy_Click(object sender, EventArgs e) { - _textBox.SelectAll(); - Clipboard.SetText(_textBox.SelectedText); +//*********************************************************// +// Copyright (c) Microsoft. All rights reserved. +// +// Apache 2.0 License +// +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//*********************************************************// + +using System; +using System.Windows.Forms; + +namespace Microsoft.NodejsTools.Commands { + public partial class DiagnosticsForm : Form { + public DiagnosticsForm(string content) { + InitializeComponent(); + _textBox.Text = content; + } + + public TextBox TextBox { + get { + return _textBox; + } + } + + private void _ok_Click(object sender, EventArgs e) { + Close(); + } + + private void _copy_Click(object sender, EventArgs e) { + _textBox.SelectAll(); + Clipboard.SetText(_textBox.SelectedText); } private void _diagnosticLoggingCheckbox_CheckedChanged(object sender, EventArgs e) { NodejsPackage.Instance.DiagnosticsOptionsPage.IsLiveDiagnosticsEnabled = _diagnosticLoggingCheckbox.Checked; } - } -} + } +} diff --git a/Nodejs/Product/Nodejs/Debugger/Communication/DebuggerConnection.cs b/Nodejs/Product/Nodejs/Debugger/Communication/DebuggerConnection.cs index d2b61f36e..1130e425e 100644 --- a/Nodejs/Product/Nodejs/Debugger/Communication/DebuggerConnection.cs +++ b/Nodejs/Product/Nodejs/Debugger/Communication/DebuggerConnection.cs @@ -1,124 +1,124 @@ -//*********************************************************// -// Copyright (c) Microsoft. All rights reserved. -// -// Apache 2.0 License -// -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -// implied. See the License for the specific language governing -// permissions and limitations under the License. -// -//*********************************************************// - -using System; -using System.Diagnostics; -using System.IO; -using System.Net.Sockets; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Microsoft.NodejsTools.Logging; -using Microsoft.VisualStudioTools; -using Microsoft.VisualStudioTools.Project; -using Newtonsoft.Json; - -namespace Microsoft.NodejsTools.Debugger.Communication { - sealed class DebuggerConnection : IDebuggerConnection { - private static readonly Encoding _encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); - private static readonly Regex _contentLengthFieldRegex = new Regex(@"Content-Length: (\d+)", RegexOptions.Compiled); - private static readonly Regex _nodeVersionFieldRegex = new Regex(@"Embedding-Host: node v([0-9.]+)", RegexOptions.Compiled); - - private readonly AsyncProducerConsumerCollection _packetsToSend = new AsyncProducerConsumerCollection(); - private readonly INetworkClientFactory _networkClientFactory; - private INetworkClient _networkClient; - private readonly object _networkClientLock = new object(); - private volatile Version _nodeVersion; - - public DebuggerConnection(INetworkClientFactory networkClientFactory) { - Utilities.ArgumentNotNull("networkClientFactory", networkClientFactory); - - _networkClientFactory = networkClientFactory; - } - - public void Dispose() { - Close(); - } - - /// - /// Close connection. - /// - public void Close() { - lock (_networkClientLock) { - if (_networkClient != null) { - _networkClient.Dispose(); - _networkClient = null; - } - } - } - - /// - /// Send a message. - /// - /// Message. - public void SendMessage(string message) { - Utilities.ArgumentNotNullOrEmpty("message", message); - - if (!Connected) { - return; - } - - LiveLogger.WriteLine("Request: " + message, typeof(DebuggerConnection)); - - var messageBody = _encoding.GetBytes(message); - var messageHeader = _encoding.GetBytes(string.Format("Content-Length: {0}\r\n\r\n", messageBody.Length)); - _packetsToSend.Add(messageHeader); - _packetsToSend.Add(messageBody); - } - - /// - /// Fired when received inbound message. - /// - public event EventHandler OutputMessage; - - /// - /// Fired when connection was closed. - /// - public event EventHandler ConnectionClosed; - - /// - /// Gets a value indicating whether connection established. - /// - public bool Connected { - get { - lock (_networkClientLock) { - return _networkClient != null && _networkClient.Connected; - } - } - } - - /// - /// Gets a Node.js version, or null if it was not supplied by the debuggee. - /// - public Version NodeVersion { - get { return _nodeVersion; } - } - - /// - /// Connect to specified debugger endpoint. - /// - /// URI identifying the endpoint to connect to. - public void Connect(Uri uri) { - Utilities.ArgumentNotNull("uri", uri); - LiveLogger.WriteLine("Debugger connecting to URI: {0}", uri); - - Close(); - lock (_networkClientLock) { - int connection_attempts = 0; - const int MAX_ATTEMPTS = 5; +//*********************************************************// +// Copyright (c) Microsoft. All rights reserved. +// +// Apache 2.0 License +// +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. +// +//*********************************************************// + +using System; +using System.Diagnostics; +using System.IO; +using System.Net.Sockets; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.NodejsTools.Logging; +using Microsoft.VisualStudioTools; +using Microsoft.VisualStudioTools.Project; +using Newtonsoft.Json; + +namespace Microsoft.NodejsTools.Debugger.Communication { + sealed class DebuggerConnection : IDebuggerConnection { + private static readonly Encoding _encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + private static readonly Regex _contentLengthFieldRegex = new Regex(@"Content-Length: (\d+)", RegexOptions.Compiled); + private static readonly Regex _nodeVersionFieldRegex = new Regex(@"Embedding-Host: node v([0-9.]+)", RegexOptions.Compiled); + + private readonly AsyncProducerConsumerCollection _packetsToSend = new AsyncProducerConsumerCollection(); + private readonly INetworkClientFactory _networkClientFactory; + private INetworkClient _networkClient; + private readonly object _networkClientLock = new object(); + private volatile Version _nodeVersion; + + public DebuggerConnection(INetworkClientFactory networkClientFactory) { + Utilities.ArgumentNotNull("networkClientFactory", networkClientFactory); + + _networkClientFactory = networkClientFactory; + } + + public void Dispose() { + Close(); + } + + /// + /// Close connection. + /// + public void Close() { + lock (_networkClientLock) { + if (_networkClient != null) { + _networkClient.Dispose(); + _networkClient = null; + } + } + } + + /// + /// Send a message. + /// + /// Message. + public void SendMessage(string message) { + Utilities.ArgumentNotNullOrEmpty("message", message); + + if (!Connected) { + return; + } + + LiveLogger.WriteLine("Request: " + message, typeof(DebuggerConnection)); + + var messageBody = _encoding.GetBytes(message); + var messageHeader = _encoding.GetBytes(string.Format("Content-Length: {0}\r\n\r\n", messageBody.Length)); + _packetsToSend.Add(messageHeader); + _packetsToSend.Add(messageBody); + } + + /// + /// Fired when received inbound message. + /// + public event EventHandler OutputMessage; + + /// + /// Fired when connection was closed. + /// + public event EventHandler ConnectionClosed; + + /// + /// Gets a value indicating whether connection established. + /// + public bool Connected { + get { + lock (_networkClientLock) { + return _networkClient != null && _networkClient.Connected; + } + } + } + + /// + /// Gets a Node.js version, or null if it was not supplied by the debuggee. + /// + public Version NodeVersion { + get { return _nodeVersion; } + } + + /// + /// Connect to specified debugger endpoint. + /// + /// URI identifying the endpoint to connect to. + public void Connect(Uri uri) { + Utilities.ArgumentNotNull("uri", uri); + LiveLogger.WriteLine("Debugger connecting to URI: {0}", uri); + + Close(); + lock (_networkClientLock) { + int connection_attempts = 0; + const int MAX_ATTEMPTS = 5; while (true) { connection_attempts++; try { @@ -155,158 +155,158 @@ public void Connect(Uri uri) { System.Threading.Thread.Sleep(200); } } - } - } - - Task.Factory.StartNew(ReceiveAndDispatchMessagesWorker); - Task.Factory.StartNew(SendPacketsWorker); - } - - /// - /// Sends packets queued by . - /// - private async void SendPacketsWorker() { - INetworkClient networkClient; - lock (_networkClientLock) { - networkClient = _networkClient; - } - if (networkClient == null) { - return; - } - - try { - var stream = networkClient.GetStream(); - while (Connected) { - byte[] packet = await _packetsToSend.TakeAsync().ConfigureAwait(false); - await stream.WriteAsync(packet, 0, packet.Length).ConfigureAwait(false); - await stream.FlushAsync().ConfigureAwait(false); - } - } catch (SocketException) { - } catch (ObjectDisposedException) { - } catch (IOException) { - } catch (Exception e) { - LiveLogger.WriteLine(string.Format("Failed to write message {0}.", e), typeof(DebuggerConnection)); - throw; - } - } - - /// - /// Receives messages from debugger, parses them to extract the body, and dispatches them to listeners. - /// - private async void ReceiveAndDispatchMessagesWorker() { - LiveLogger.WriteLine("Established connection.", typeof(DebuggerConnection)); - - INetworkClient networkClient; - lock (_networkClientLock) { - networkClient = _networkClient; - } - if (networkClient == null) { - return; - } - - try { - var stream = networkClient.GetStream(); - - // Use a single read buffer and a single StringBuilder (periodically cleared) across loop iterations, - // to avoid costly repeated allocations. - var buffer = new byte[0x1000]; - var sb = new StringBuilder(); - - // Read and process incoming messages until disconnected. - while (true) { - // Read the header of this message. - int contentLength = 0; - while (true) { - // Read a single header field. - string field; - sb.Clear(); - while (true) { - int bytesRead = await stream.ReadAsync(buffer, 0, 1).ConfigureAwait(false); - if (bytesRead < 1) { - // End of stream - we are disconnected from debuggee. - throw new EndOfStreamException(); - } - - // All fields that we care about are ASCII, and for all the other fields we only need to recognize - // the trailing \r\n, so there's no need to do proper decoding here. - sb.Append((char)buffer[0]); - - // "\r\n" terminates the field. - if (sb.Length >= 2 && sb[sb.Length - 2] == '\r' && sb[sb.Length - 1] == '\n') { - field = sb.ToString(0, sb.Length - 2); - break; - } - } - - // Blank line terminates the header. - if (string.IsNullOrEmpty(field)) { - break; - } - - // Otherwise, it's an actual field. Parse it if it's something we care about. - - // Content-Length - var match = _contentLengthFieldRegex.Match(field); - if (match.Success) { - int.TryParse(match.Groups[1].Value, out contentLength); - continue; - } - - // Embedding-Host, which contains the Node.js version number. Only try parsing that if we don't know the version yet - - // it normally comes in the very first packet, so this saves time trying to parse all the consequent ones. - if (NodeVersion == null) { - match = _nodeVersionFieldRegex.Match(field); - if (match.Success) { - Version nodeVersion; - Version.TryParse(match.Groups[1].Value, out nodeVersion); - _nodeVersion = nodeVersion; - } - } - } - - if (contentLength == 0) { - continue; - } - - // Read the body of this message. - - // If our preallocated buffer is large enough, use it - this should be true for vast majority of messages. - // If not, allocate a buffer that is large enough and use that, then throw it away - don't replace the original - // buffer with it, so that we don't hold onto a huge chunk of memory for the rest of the debugging session just - // because of a single long message. - var bodyBuffer = buffer.Length >= contentLength ? buffer : new byte[contentLength]; - - for (int i = 0; i < contentLength; ) { - i += await stream.ReadAsync(bodyBuffer, i, contentLength - i).ConfigureAwait(false); - } - - string message = _encoding.GetString(bodyBuffer, 0, contentLength); - LiveLogger.WriteLine("Response: " + message, typeof(DebuggerConnection)); - - // Notify subscribers. - var outputMessage = OutputMessage; - if (outputMessage != null) { - outputMessage(this, new MessageEventArgs(message)); - } - } - } catch (SocketException) { - } catch (IOException) { - } catch (ObjectDisposedException) { - } catch (DecoderFallbackException ex) { - LiveLogger.WriteLine(string.Format("Error decoding response body: {0}", ex), typeof(DebuggerConnection)); - } catch (JsonReaderException ex) { - LiveLogger.WriteLine(string.Format("Error parsing JSON response: {0}", ex), typeof(DebuggerConnection)); - } catch (Exception ex) { - LiveLogger.WriteLine(string.Format("Message processing failed: {0}", ex), typeof(DebuggerConnection)); - throw; - } finally { - LiveLogger.WriteLine("Connection was closed.", typeof(DebuggerConnection)); - - var connectionClosed = ConnectionClosed; - if (connectionClosed != null) { - connectionClosed(this, EventArgs.Empty); - } - } - } - } + } + } + + Task.Factory.StartNew(ReceiveAndDispatchMessagesWorker); + Task.Factory.StartNew(SendPacketsWorker); + } + + /// + /// Sends packets queued by . + /// + private async void SendPacketsWorker() { + INetworkClient networkClient; + lock (_networkClientLock) { + networkClient = _networkClient; + } + if (networkClient == null) { + return; + } + + try { + var stream = networkClient.GetStream(); + while (Connected) { + byte[] packet = await _packetsToSend.TakeAsync().ConfigureAwait(false); + await stream.WriteAsync(packet, 0, packet.Length).ConfigureAwait(false); + await stream.FlushAsync().ConfigureAwait(false); + } + } catch (SocketException) { + } catch (ObjectDisposedException) { + } catch (IOException) { + } catch (Exception e) { + LiveLogger.WriteLine(string.Format("Failed to write message {0}.", e), typeof(DebuggerConnection)); + throw; + } + } + + /// + /// Receives messages from debugger, parses them to extract the body, and dispatches them to listeners. + /// + private async void ReceiveAndDispatchMessagesWorker() { + LiveLogger.WriteLine("Established connection.", typeof(DebuggerConnection)); + + INetworkClient networkClient; + lock (_networkClientLock) { + networkClient = _networkClient; + } + if (networkClient == null) { + return; + } + + try { + var stream = networkClient.GetStream(); + + // Use a single read buffer and a single StringBuilder (periodically cleared) across loop iterations, + // to avoid costly repeated allocations. + var buffer = new byte[0x1000]; + var sb = new StringBuilder(); + + // Read and process incoming messages until disconnected. + while (true) { + // Read the header of this message. + int contentLength = 0; + while (true) { + // Read a single header field. + string field; + sb.Clear(); + while (true) { + int bytesRead = await stream.ReadAsync(buffer, 0, 1).ConfigureAwait(false); + if (bytesRead < 1) { + // End of stream - we are disconnected from debuggee. + throw new EndOfStreamException(); + } + + // All fields that we care about are ASCII, and for all the other fields we only need to recognize + // the trailing \r\n, so there's no need to do proper decoding here. + sb.Append((char)buffer[0]); + + // "\r\n" terminates the field. + if (sb.Length >= 2 && sb[sb.Length - 2] == '\r' && sb[sb.Length - 1] == '\n') { + field = sb.ToString(0, sb.Length - 2); + break; + } + } + + // Blank line terminates the header. + if (string.IsNullOrEmpty(field)) { + break; + } + + // Otherwise, it's an actual field. Parse it if it's something we care about. + + // Content-Length + var match = _contentLengthFieldRegex.Match(field); + if (match.Success) { + int.TryParse(match.Groups[1].Value, out contentLength); + continue; + } + + // Embedding-Host, which contains the Node.js version number. Only try parsing that if we don't know the version yet - + // it normally comes in the very first packet, so this saves time trying to parse all the consequent ones. + if (NodeVersion == null) { + match = _nodeVersionFieldRegex.Match(field); + if (match.Success) { + Version nodeVersion; + Version.TryParse(match.Groups[1].Value, out nodeVersion); + _nodeVersion = nodeVersion; + } + } + } + + if (contentLength == 0) { + continue; + } + + // Read the body of this message. + + // If our preallocated buffer is large enough, use it - this should be true for vast majority of messages. + // If not, allocate a buffer that is large enough and use that, then throw it away - don't replace the original + // buffer with it, so that we don't hold onto a huge chunk of memory for the rest of the debugging session just + // because of a single long message. + var bodyBuffer = buffer.Length >= contentLength ? buffer : new byte[contentLength]; + + for (int i = 0; i < contentLength; ) { + i += await stream.ReadAsync(bodyBuffer, i, contentLength - i).ConfigureAwait(false); + } + + string message = _encoding.GetString(bodyBuffer, 0, contentLength); + LiveLogger.WriteLine("Response: " + message, typeof(DebuggerConnection)); + + // Notify subscribers. + var outputMessage = OutputMessage; + if (outputMessage != null) { + outputMessage(this, new MessageEventArgs(message)); + } + } + } catch (SocketException) { + } catch (IOException) { + } catch (ObjectDisposedException) { + } catch (DecoderFallbackException ex) { + LiveLogger.WriteLine(string.Format("Error decoding response body: {0}", ex), typeof(DebuggerConnection)); + } catch (JsonReaderException ex) { + LiveLogger.WriteLine(string.Format("Error parsing JSON response: {0}", ex), typeof(DebuggerConnection)); + } catch (Exception ex) { + LiveLogger.WriteLine(string.Format("Message processing failed: {0}", ex), typeof(DebuggerConnection)); + throw; + } finally { + LiveLogger.WriteLine("Connection was closed.", typeof(DebuggerConnection)); + + var connectionClosed = ConnectionClosed; + if (connectionClosed != null) { + connectionClosed(this, EventArgs.Empty); + } + } + } + } } \ No newline at end of file