Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publish generated HTML in the client #1633

Merged
merged 3 commits into from
Feb 27, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,11 @@ internal static RazorHtmlDocument GetHtmlDocument(this RazorCodeDocument documen
if (razorHtmlObj == null)
{
var razorHtmlDocument = RazorHtmlWriter.GetHtmlDocument(document);
document.Items[typeof(RazorHtmlDocument)] = razorHtmlDocument;

return razorHtmlDocument;
if (razorHtmlDocument != null)
NTaylorMullen marked this conversation as resolved.
Show resolved Hide resolved
{
document.Items[typeof(RazorHtmlDocument)] = razorHtmlDocument;
return razorHtmlDocument;
}
}

return (RazorHtmlDocument)razorHtmlObj;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@ public static class LanguageServerConstants
public const string ProjectConfigurationFile = "project.razor.json";

public const string RazorRangeFormattingEndpoint = "razor/rangeFormatting";

public const string RazorUpdateCSharpBufferEndpoint = "razor/updateCSharpBuffer";

public const string RazorUpdateHtmlBufferEndpoint = "razor/updateHtmlBuffer";
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@

namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class DefaultGeneratedCodeContainerStore : GeneratedCodeContainerStore
internal class DefaultGeneratedDocumentContainerStore : GeneratedDocumentContainerStore
{
private readonly ConcurrentDictionary<string, GeneratedCodeContainer> _store;
private readonly ConcurrentDictionary<string, GeneratedDocumentContainer> _store;
private readonly ForegroundDispatcher _foregroundDispatcher;
private readonly DocumentVersionCache _documentVersionCache;
private readonly CSharpPublisher _csharpPublisher;
private readonly GeneratedDocumentPublisher _generatedDocumentPublisher;
private ProjectSnapshotManagerBase _projectSnapshotManager;

public DefaultGeneratedCodeContainerStore(
public DefaultGeneratedDocumentContainerStore(
ForegroundDispatcher foregroundDispatcher,
DocumentVersionCache documentVersionCache,
CSharpPublisher csharpPublisher)
GeneratedDocumentPublisher generatedDocumentPublisher)
{
if (foregroundDispatcher == null)
{
Expand All @@ -33,18 +33,18 @@ public DefaultGeneratedCodeContainerStore(
throw new ArgumentNullException(nameof(documentVersionCache));
}

if (csharpPublisher is null)
if (generatedDocumentPublisher is null)
{
throw new ArgumentNullException(nameof(csharpPublisher));
throw new ArgumentNullException(nameof(generatedDocumentPublisher));
}

_foregroundDispatcher = foregroundDispatcher;
_documentVersionCache = documentVersionCache;
_csharpPublisher = csharpPublisher;
_store = new ConcurrentDictionary<string, GeneratedCodeContainer>(FilePathComparer.Instance);
_generatedDocumentPublisher = generatedDocumentPublisher;
_store = new ConcurrentDictionary<string, GeneratedDocumentContainer>(FilePathComparer.Instance);
}

public override GeneratedCodeContainer Get(string physicalFilePath)
public override GeneratedDocumentContainer Get(string physicalFilePath)
{
if (physicalFilePath == null)
{
Expand Down Expand Up @@ -86,14 +86,14 @@ internal void ProjectSnapshotManager_Changed(object sender, ProjectChangeEventAr
}
}

private GeneratedCodeContainer Create(string filePath)
private GeneratedDocumentContainer Create(string filePath)
{
var codeContainer = new GeneratedCodeContainer();
codeContainer.GeneratedCodeChanged += (sender, args) =>
var documentContainer = new GeneratedDocumentContainer();
documentContainer.GeneratedCSharpChanged += (sender, args) =>
{
var generatedCodeContainer = (GeneratedCodeContainer)sender;
var generatedDocumentContainer = (GeneratedDocumentContainer)sender;

var latestDocument = generatedCodeContainer.LatestDocument;
var latestDocument = generatedDocumentContainer.LatestDocument;

Task.Factory.StartNew(() =>
{
Expand All @@ -109,11 +109,35 @@ private GeneratedCodeContainer Create(string filePath)
return;
}

_csharpPublisher.Publish(filePath, args.NewText, hostDocumentVersion);
_generatedDocumentPublisher.PublishCSharp(filePath, args.NewText, hostDocumentVersion);
}, CancellationToken.None, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler);
};

return codeContainer;
documentContainer.GeneratedHtmlChanged += (sender, args) =>
{
var generatedDocumentContainer = (GeneratedDocumentContainer)sender;

var latestDocument = generatedDocumentContainer.LatestDocument;

Task.Factory.StartNew(() =>
{
if (!_projectSnapshotManager.IsDocumentOpen(filePath))
{
// Document isn't opened, no need to notify the client
return;
}

if (!_documentVersionCache.TryGetDocumentVersion(latestDocument, out var hostDocumentVersion))
{
// Cache entry doesn't exist, document most likely was evicted from the cache/too old.
return;
}

_generatedDocumentPublisher.PublishHtml(filePath, args.NewText, hostDocumentVersion);
NTaylorMullen marked this conversation as resolved.
Show resolved Hide resolved
}, CancellationToken.None, TaskCreationOptions.None, _foregroundDispatcher.ForegroundScheduler);
};

return documentContainer;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.LanguageServer.Common;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Text;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;

namespace Microsoft.AspNetCore.Razor.LanguageServer
{
internal class DefaultGeneratedDocumentPublisher : GeneratedDocumentPublisher
{
private static readonly SourceText EmptySourceText = SourceText.From(string.Empty);
private readonly Dictionary<string, SourceText> _publishedCSharpSourceText;
private readonly Dictionary<string, SourceText> _publishedHtmlSourceText;
private readonly Lazy<ILanguageServer> _server;
private readonly ForegroundDispatcher _foregroundDispatcher;
private ProjectSnapshotManagerBase _projectSnapshotManager;

public DefaultGeneratedDocumentPublisher(
ForegroundDispatcher foregroundDispatcher,
Lazy<ILanguageServer> server)
{
if (foregroundDispatcher is null)
{
throw new ArgumentNullException(nameof(foregroundDispatcher));
}

if (server is null)
{
throw new ArgumentNullException(nameof(server));
}

_foregroundDispatcher = foregroundDispatcher;
_server = server;
_publishedCSharpSourceText = new Dictionary<string, SourceText>(FilePathComparer.Instance);
_publishedHtmlSourceText = new Dictionary<string, SourceText>(FilePathComparer.Instance);
}

public override void Initialize(ProjectSnapshotManagerBase projectManager)
{
_projectSnapshotManager = projectManager;
_projectSnapshotManager.Changed += ProjectSnapshotManager_Changed;
}

public override void PublishCSharp(string filePath, SourceText sourceText, long hostDocumentVersion)
{
if (filePath is null)
{
throw new ArgumentNullException(nameof(filePath));
}

if (sourceText is null)
{
throw new ArgumentNullException(nameof(sourceText));
}

_foregroundDispatcher.AssertForegroundThread();

if (!_publishedCSharpSourceText.TryGetValue(filePath, out var previouslyPublishedText))
{
previouslyPublishedText = EmptySourceText;
}

IReadOnlyList<TextChange> textChanges = Array.Empty<TextChange>();
if (!sourceText.ContentEquals(previouslyPublishedText))
{
textChanges = sourceText.GetTextChanges(previouslyPublishedText);
}

_publishedCSharpSourceText[filePath] = sourceText;

var request = new UpdateBufferRequest()
NTaylorMullen marked this conversation as resolved.
Show resolved Hide resolved
{
HostDocumentFilePath = filePath,
Changes = textChanges,
HostDocumentVersion = hostDocumentVersion,
};

_server.Value.Client.SendRequest(LanguageServerConstants.RazorUpdateCSharpBufferEndpoint, request);
}

public override void PublishHtml(string filePath, SourceText sourceText, long hostDocumentVersion)
{
if (filePath is null)
{
throw new ArgumentNullException(nameof(filePath));
}

if (sourceText is null)
{
throw new ArgumentNullException(nameof(sourceText));
}

_foregroundDispatcher.AssertForegroundThread();

if (!_publishedHtmlSourceText.TryGetValue(filePath, out var previouslyPublishedText))
{
previouslyPublishedText = EmptySourceText;
}

IReadOnlyList<TextChange> textChanges = Array.Empty<TextChange>();
if (!sourceText.ContentEquals(previouslyPublishedText))
{
textChanges = sourceText.GetTextChanges(previouslyPublishedText);
}

_publishedHtmlSourceText[filePath] = sourceText;

var request = new UpdateBufferRequest()
{
HostDocumentFilePath = filePath,
Changes = textChanges,
HostDocumentVersion = hostDocumentVersion,
};

_server.Value.Client.SendRequest(LanguageServerConstants.RazorUpdateHtmlBufferEndpoint, request);
}

private void ProjectSnapshotManager_Changed(object sender, ProjectChangeEventArgs args)
{
_foregroundDispatcher.AssertForegroundThread();

switch (args.Kind)
{
case ProjectChangeKind.DocumentChanged:
if (!_projectSnapshotManager.IsDocumentOpen(args.DocumentFilePath))
{
// Document closed, evict published source text.
if (_publishedCSharpSourceText.ContainsKey(args.DocumentFilePath))
{
var removed = _publishedCSharpSourceText.Remove(args.DocumentFilePath);
Debug.Assert(removed, "Published source text should be protected by the foreground thread and should never fail to remove.");
}
if (_publishedHtmlSourceText.ContainsKey(args.DocumentFilePath))
{
var removed = _publishedHtmlSourceText.Remove(args.DocumentFilePath);
Debug.Assert(removed, "Published source text should be protected by the foreground thread and should never fail to remove.");
}
}
break;
case ProjectChangeKind.DocumentRemoved:
// Document removed, evict published source text.
if (_publishedCSharpSourceText.ContainsKey(args.DocumentFilePath))
{
var removed = _publishedCSharpSourceText.Remove(args.DocumentFilePath);
Debug.Assert(removed, "Published source text should be protected by the foreground thread and should never fail to remove.");
}
if (_publishedHtmlSourceText.ContainsKey(args.DocumentFilePath))
{
var removed = _publishedHtmlSourceText.Remove(args.DocumentFilePath);
Debug.Assert(removed, "Published source text should be protected by the foreground thread and should never fail to remove.");
}
break;
}
}
}
}
Loading