void OnClosed(object sender, EventArgs e) { if (ReplayHost == null) { return; } ReplayHost.AdornmentChanged -= ReplayHostAdornmentChanged; ReplayHost.Erred -= ReplayHostErred; ReplayHost toDispose = null; lock (ReplayHosts) { var t = ReplayHosts[Project.OutputFilePath]; t = Tuple.Create(t.Item1, t.Item2 - 1); if (t.Item2 == 0) { ReplayHosts.Remove(Project.OutputFilePath); toDispose = t.Item1; } else { ReplayHosts[Project.OutputFilePath] = t; } } toDispose?.Dispose(); ReplayHost = null; }
static async Task TestClientAsync() { //var workspace = Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace.Create(); //var solution = await workspace.OpenSolutionAsync(@"C:\Users\lwischik\Documents\Visual Studio 2015\Projects\ConsoleApplicationCS\ConsoleApplicationCS.sln"); //var project = solution.Projects.Single(); var workspace = new Microsoft.DotNet.ProjectModel.Workspaces.ProjectJsonWorkspace(SampleProjectsDirectory + "/ConsoleApp1"); var solution = workspace.CurrentSolution; var project = solution.Projects.Single(); var txt = "int x = 15;\r\nint y = x+2;\r\nSystem.Console.WriteLine(y);\r\n"; project = project.AddDocument("a.csx", txt, null, "c:\\a.csx").WithSourceCodeKind(SourceCodeKind.Script).Project; project = await ReplayHost.InstrumentProjectAsync(project, ImmutableArray <Diagnostic> .Empty, CancellationToken.None); var document = project.Documents.FirstOrDefault(d => Path.GetFileName(d.FilePath) == "a.csx"); if (document != null) { Console.WriteLine($"{document.FilePath}\r\n{await document.GetTextAsync()}"); } var result = await ReplayHost.BuildAsync(project, CancellationToken.None); foreach (var d in result.Diagnostics) { if (d.Severity != DiagnosticSeverity.Error && d.Severity != DiagnosticSeverity.Warning) { continue; } var path = d.Location.IsInSource ? Path.GetFileName(d.Location.SourceTree.FilePath) : ""; var line = d.Location.IsInSource ? d.Location.GetMappedLineSpan().StartLinePosition.Line.ToString() : ""; Console.WriteLine($"{path}({line}):{d.GetMessage()}"); } if (!result.Success) { return; } var process = await ReplayHost.LaunchProcessAsync(result.ReplayOutputFilePath, CancellationToken.None); var cts = new CancellationTokenSource(); var task = Runner(process, cts.Token); var cmd = $"WATCH\t{document.FilePath}\t1\t40\t0"; Console.WriteLine(cmd); await process.PostLineAsync(cmd, CancellationToken.None); while (true) { cmd = await Task.Run(Console.In.ReadLineAsync); if (cmd == null) { break; } await process.PostLineAsync(cmd, CancellationToken.None); } cts.Cancel(); await task.IgnoreCancellation(); }
static async Task TestScriptInstrumentingAsync() { var workspace = new AdhocWorkspace(); var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), "TestProject", "TestProject", LanguageNames.CSharp); var project = workspace.AddProject(projectInfo); var src = SourceText.From(File.ReadAllText(@"C:\Users\ljw10\Documents\Visual Studio 2015\Projects\ScriptApplicationCS\CodeFile1.csx")); var document = workspace.AddDocument(project.Id, "CodeFile1.csx", src).WithSourceCodeKind(SourceCodeKind.Script); document = await ReplayHost.InstrumentDocumentAsync(document, null, null, CancellationToken.None); Console.WriteLine($"{document.FilePath}\r\n{await document.GetTextAsync()}"); }
bool Initialize() { if (DocumentId == null) { var dte = Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(EnvDTE.DTE)) as EnvDTE.DTE; var activeDocument = dte?.ActiveDocument; // sometimes we're constructed/invoked before ActiveDocument has been set if (activeDocument != null) { DocumentId = Workspace.CurrentSolution.GetDocumentIdsWithFilePath(activeDocument.FullName).FirstOrDefault(); } if (DocumentId == null) { return(false); } } var document = Workspace.CurrentSolution.GetDocument(DocumentId); var project = document?.Project; if (project == null) { return(false); } if (ReplayHost == null) { lock (ReplayHosts) { Tuple <ReplayHost, int> t; if (ReplayHosts.TryGetValue(project.OutputFilePath, out t)) { t = Tuple.Create(t.Item1, t.Item2 + 1); } else { t = Tuple.Create(new ReplayHost(false), 1); } ReplayHosts[project.OutputFilePath] = t; ReplayHost = t.Item1; } ReplayHost.AdornmentChanged += ReplayHostAdornmentChanged; ReplayHost.Erred += ReplayHostErred; var dummy = ReplayHost.ChangeDocumentAsync(Project, null, -1, -1, -1); } return(true); }
static async Task TestCodeInstrumentingAsync() { var workspace = new AdhocWorkspace(); var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), VersionStamp.Create(), "TestProject", "TestProject", LanguageNames.CSharp); var project = workspace.AddProject(projectInfo); var txt = @" using System; class Program { static void Main() { Console.WriteLine(""hello""); int x = 2; Console.WriteLine(x); } } "; var document = workspace.AddDocument(project.Id, "program.cs", SourceText.From(txt)); document = await ReplayHost.InstrumentDocumentAsync(document, null, null, CancellationToken.None); Console.WriteLine($"{document.FilePath}\r\n{await document.GetTextAsync()}"); }
static async Task TestHostAsync(string projectName) { Project project; if (projectName == "ConsoleApp1") { var workspace = new Microsoft.DotNet.ProjectModel.Workspaces.ProjectJsonWorkspace(SampleProjectsDirectory + "/ConsoleApp1"); var solution = workspace.CurrentSolution; project = solution.Projects.Single(); var txt = "int x = 15;\r\nint y = x+2;\r\nSystem.Console.WriteLine(y);\r\n"; var document = project.AddDocument("a.csx", txt, null, "c:\\a.csx").WithSourceCodeKind(SourceCodeKind.Script); project = document.Project; } else if (projectName == "Methods") { project = await ScriptWorkspace.FromDirectoryScanAsync(SampleProjectsDirectory + "/Methods"); } else { throw new ArgumentException("Projects 'ConsoleApp1' and 'Methods' both work", nameof(projectName)); } var host = new ReplayHost(false); host.DiagnosticChanged += (isAdd, tag, diagnostic, deferrable, cancel) => { if (isAdd) { Console.WriteLine($"+D{tag}: {diagnostic.GetMessage()}"); } else { Console.WriteLine($"-D{tag}"); } }; host.AdornmentChanged += (isAdd, tag, file, line, content, deferrable, cancel) => { if (isAdd) { Console.WriteLine($"+A{tag}: {Path.GetFileName(file)}({line}) {content}"); } else { Console.WriteLine($"-A{tag}"); } }; host.Erred += (error, deferrable, cancel) => { Console.WriteLine(error); }; Console.WriteLine("PROJECT"); await host.ChangeDocumentAsync(project, null, 0, 0, 0); Console.WriteLine("VIEW"); await host.WatchAsync(); if (projectName == "ConsoleApp1") { Console.WriteLine("CHANGE"); var txt = "int x = 15;\r\nint y = x+2;d\r\nSystem.Console.WriteLine(y);\r\n"; var document = project.Documents.First(d => d.Name == "a.csx"); document = document.WithText(SourceText.From(txt)); project = document.Project; await host.ChangeDocumentAsync(project, "a.csx", 1, 1, 1); } else if (projectName == "Methods") { Console.WriteLine("CHANGE MARKDOWN"); var document = project.Documents.First(d => d.Name == "methods.md"); var src = document.GetTextAsync().Result; var txt = src.ToString(); int i = src.Lines.FindIndex(line => txt.Substring(line.Span.Start, line.Span.Length) == "Introductory prose"); txt = txt.Replace("Introductory prose", "Some\nintroduction."); document = document.WithText(SourceText.From(txt)); project = document.Project; // document = project.Documents.First(d => d.Name == "methods.md.csx"); txt = ScriptWorkspace.Md2Csx("methods.md", txt); document = document.WithText(SourceText.From(txt)); project = document.Project; await host.ChangeDocumentAsync(project, "methods.md", i, 1, 2); Console.WriteLine("VIEW"); await host.WatchAsync(); Console.WriteLine("CHANGE CODE"); document = project.Documents.First(d => d.Name == "methods.md"); src = document.GetTextAsync().Result; txt = src.ToString(); i = src.Lines.FindIndex(line => txt.Substring(line.Span.Start, line.Span.Length) == "var txt = GetText();"); txt = txt.Replace("var txt = GetText();", "var txt = GetText();\n"); document = document.WithText(SourceText.From(txt)); project = document.Project; // document = project.Documents.First(d => d.Name == "methods.md.csx"); src = document.GetTextAsync().Result; txt = src.ToString(); txt = txt.Replace("var txt = GetText();", "var txt = GetText();\n"); document = document.WithText(SourceText.From(txt)); project = document.Project; // await host.ChangeDocumentAsync(project, "methods.md", i, 1, 2); } Console.WriteLine("DONE"); }
public static async Task DoSocketAsync(HttpContext http, WebSocket socket) { Exception _ex = null; try { if (!http.Request.Path.HasValue) { await socket.SendStringAsync("ERROR\tNo project specified"); return; } var dir = Directory.GetCurrentDirectory(); for (; dir != null; dir = Path.GetDirectoryName(dir)) { if (Directory.Exists(dir + "/SampleProjects")) { break; } } dir = Path.GetFullPath(dir + "/SampleProjects" + http.Request.Path.Value); if (!Directory.Exists(dir)) { await socket.SendStringAsync($"ERROR\tProject doesn't exist '{dir}'"); return; } // Load the project, and establish the "OK" handshake to show we've done it var project = await ScriptWorkspace.FromDirectoryScanAsync(dir); await socket.SendStringAsync("OK"); var cmd = await socket.RecvStringAsync(); if (cmd != "OK") { await socket.SendStringAsync($"ERROR\tExpected 'OK' not '{cmd}'"); return; } // Set up monitoring for diagnostics var host = new ReplayHost(true); ReplayHost.AdornmentChangedHandler lambdaAdornmentChanged = async(isAdd, tag, file, line, content, deferrable, cancel) => { // ADORNMENT remove 7 // ADORNMENT ADD 7 231 Hello world var deferral = deferrable.GetDeferral(); var msg = isAdd ? $"ADORNMENT\tadd\t{tag}\t{file}\t{line+1}\t{content}" : $"ADORNMENT\tremove\t{tag}\t{file}"; if (socket.State != WebSocketState.Closed) { await socket.SendStringAsync(msg); } deferral.Complete(); }; ReplayHost.DiagnosticChangedHandler lambdaDiagnosticChanged = async(isAdd, tag, diagnostic, deferrable, cancel) => { // DIAGNOSTIC remove 7 file.cs // DIAGNOSTIC add 7 file.cs Hidden startLine startCol length msg var deferral = deferrable.GetDeferral(); string msg; if (isAdd) { var file = ""; int startLine = -1, startCol = -1, length = 0; if (diagnostic.Location.IsInSource) { var loc = diagnostic.Location.GetMappedLineSpan(); file = loc.HasMappedPath ? loc.Path : diagnostic.Location.SourceTree.FilePath; startLine = loc.StartLinePosition.Line + 1; startCol = loc.StartLinePosition.Character + 1; length = diagnostic.Location.SourceSpan.Length; } msg = $"DIAGNOSTIC\tadd\t{tag}\t{file}\t{diagnostic.Severity}\t{startLine}\t{startCol}\t{length}\t{diagnostic.Id}: {diagnostic.GetMessage()}"; } else { msg = $"DIAGNOSTIC\tremove\t{tag}"; } if (socket.State != WebSocketState.Closed) { await socket.SendStringAsync(msg); } deferral.Complete(); }; ReplayHost.ReplayHostError lambdaErred = async(error, deferrable, cancel) => { var deferral = deferrable.GetDeferral(); if (socket.State != WebSocketState.Closed) { await socket.SendStringAsync($"ERROR\tCLIENT: {error}"); } deferral.Complete(); }; host.AdornmentChanged += lambdaAdornmentChanged; host.DiagnosticChanged += lambdaDiagnosticChanged; host.Erred += lambdaErred; var dummy = host.ChangeDocumentAsync(project, null, -1, -1, -1); // Run the conversation! while (true) { cmd = await socket.RecvStringAsync(); if (cmd == null) { host.AdornmentChanged -= lambdaAdornmentChanged; host.DiagnosticChanged -= lambdaDiagnosticChanged; host.Erred -= lambdaErred; return; } var cmds = cmd.Split(new[] { '\t' }); if (cmds[0] == "GET") { if (cmds.Length != 2) { await socket.SendStringAsync($"ERROR\tExpected 'GET fn', not '{cmd}'"); return; } var document = project.Documents.SingleOrDefault(d => d.Name == cmds[1]); if (document == null) { await socket.SendStringAsync($"ERROR\tFile doesn't exist '{cmds[1]}'"); return; } var s = (await document.GetTextAsync()).ToString().Replace("\\", "\\\\").Replace("\r", "\\r").Replace("\n", "\\n"); await socket.SendStringAsync($"GOT\t{cmds[1]}\t{s}"); } else if (cmds[0] == "CHANGE") { string file, newContent; int startLine, startCol, oldLineCount, newLineCount, oldLength; Document document; if (cmds.Length != 8 || (file = cmds[1]) == null || (document = project.Documents.SingleOrDefault(d => d.Name == file)) == null || !int.TryParse(cmds[2], out startLine) || !int.TryParse(cmds[3], out startCol) || !int.TryParse(cmds[4], out oldLineCount) || !int.TryParse(cmds[5], out newLineCount) || !int.TryParse(cmds[6], out oldLength) || (newContent = cmds[7].Replace("\\n", "\n").Replace("\\r", "\r").Replace("\\\\", "\\")) == null) { await socket.SendStringAsync($"ERROR\tExpected 'CHANGE file startLine startCol oldLineCount newLineCount oldLength newContent', not '{cmd}'"); continue; } // var txt = await document.GetTextAsync(); var startPosition = txt.Lines[startLine - 1].Start + startCol - 1; var change = new TextChange(new TextSpan(startPosition, oldLength), newContent); txt = txt.WithChanges(change); document = document.WithText(txt); project = document.Project; if (file.EndsWith(".md")) { var csx = ScriptWorkspace.Md2Csx(file, txt.ToString()); var csxDocument = project.Documents.Single(d => d.Name == file + ".csx"); csxDocument = csxDocument.WithText(SourceText.From(csx)); project = csxDocument.Project; } // dummy = host.ChangeDocumentAsync(project, document.FilePath, startLine - 1, oldLineCount, newLineCount); } else if (cmds[0] == "WATCH") { string file; int line = -1, count = -1; Document document = null; if ((cmds.Length != 4 && cmds.Length != 2) || (file = cmds[1]) == null || (file != "*" && (document = project.Documents.Single(d => d.Name == file)) == null) || (cmds.Length == 4 && !int.TryParse(cmds[2], out line)) || (cmds.Length == 4 && !int.TryParse(cmds[3], out count))) { await socket.SendStringAsync($"ERROR\tExpected 'WATCH file line count', got '{cmd}'"); continue; } // dummy = host.WatchAsync(file == "*" ? file : document.FilePath, line, count); } else { await socket.SendStringAsync($"ERROR\tServer doesn't recognize command '{cmd}'"); } } } catch (Exception ex) { _ex = ex; } if (_ex != null) { await socket.SendStringAsync($"ERROR\t{_ex.Message} - {_ex.StackTrace.Replace("\r\n"," || ").Replace("\r"," || ").Replace("\n"," || ")}"); } }