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); }
async void OnTextBufferChanged(object sender, TextContentChangedEventArgs e) { if (!Initialize()) { return; } foreach (var change in e.Changes) { var start = e.Before.GetLineNumberFromPosition(change.OldPosition); var count = e.Before.GetLineNumberFromPosition(change.OldPosition + change.OldLength) - start; var start2 = e.After.GetLineNumberFromPosition(change.NewPosition); var newcount = e.After.GetLineNumberFromPosition(change.NewPosition + change.NewLength) - start2; if (start != start2) { Debug.WriteLine("oops!"); } await ReplayHost.ChangeDocumentAsync(Project, Document.FilePath, start, count, newcount); } }
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"," || ")}"); } }