private (RazorLanguageKind, TextEdit[]) GetFormattedEdits(RazorCodeDocument codeDocument, string expected, int positionBeforeTriggerChar)
        {
            var mappingService = new DefaultRazorDocumentMappingService();
            var languageKind   = mappingService.GetLanguageKind(codeDocument, positionBeforeTriggerChar);

            var expectedText = SourceText.From(expected);

            var(expectedCodeDocument, _) = CreateCodeDocumentAndSnapshot(expectedText, codeDocument.Source.FilePath, fileKind: codeDocument.GetFileKind());

            var edits = Array.Empty <TextEdit>();

            if (languageKind == RazorLanguageKind.CSharp)
            {
                var beforeCSharpText = SourceText.From(codeDocument.GetCSharpDocument().GeneratedCode);
                var afterCSharpText  = SourceText.From(expectedCodeDocument.GetCSharpDocument().GeneratedCode);
                edits = SourceTextDiffer.GetMinimalTextChanges(beforeCSharpText, afterCSharpText, lineDiffOnly: false).Select(c => c.AsTextEdit(beforeCSharpText)).ToArray();
            }
            else if (languageKind == RazorLanguageKind.Html)
            {
                var beforeHtmlText = SourceText.From(codeDocument.GetHtmlDocument().GeneratedHtml);
                var afterHtmlText  = SourceText.From(expectedCodeDocument.GetHtmlDocument().GeneratedHtml);
                edits = SourceTextDiffer.GetMinimalTextChanges(beforeHtmlText, afterHtmlText, lineDiffOnly: false).Select(c => c.AsTextEdit(beforeHtmlText)).ToArray();
            }

            return(languageKind, edits);
        }
Exemple #2
0
        public static SourceText GetHtmlSourceText(this RazorCodeDocument document)
        {
            if (document == null)
            {
                throw new ArgumentNullException(nameof(document));
            }

            var sourceTextObj = document.Items[HtmlSourceTextKey];

            if (sourceTextObj == null)
            {
                var htmlDocument = document.GetHtmlDocument();
                var sourceText   = SourceText.From(htmlDocument.GeneratedHtml);
                document.Items[HtmlSourceTextKey] = sourceText;

                return(sourceText);
            }

            return((SourceText)sourceTextObj);
        }
        public bool TrySetOutput(
            DefaultDocumentSnapshot document,
            RazorCodeDocument codeDocument,
            VersionStamp inputVersion,
            VersionStamp outputCSharpVersion,
            VersionStamp outputHtmlVersion)
        {
            lock (_setOutputLock)
            {
                if (_inputVersion.HasValue &&
                    _inputVersion.Value != inputVersion &&
                    _inputVersion == _inputVersion.Value.GetNewerVersion(inputVersion))
                {
                    // Latest document is newer than the provided document.
                    return(false);
                }

                if (!document.TryGetText(out var source))
                {
                    Debug.Fail("The text should have already been evaluated.");
                    return(false);
                }

                _source              = source;
                _inputVersion        = inputVersion;
                _outputCSharpVersion = outputCSharpVersion;
                _outputHtmlVersion   = outputHtmlVersion;
                _outputCSharp        = codeDocument.GetCSharpDocument();
                _outputHtml          = codeDocument.GetHtmlDocument();
                _latestDocument      = document;
                var csharpSourceText = codeDocument.GetCSharpSourceText();
                _csharpTextContainer.SetText(csharpSourceText);
                var htmlSourceText = codeDocument.GetHtmlSourceText();
                _htmlTextContainer.SetText(htmlSourceText);

                return(true);
            }
        }
            private async Task <(RazorCodeDocument, VersionStamp, VersionStamp, VersionStamp)> GetGeneratedOutputAndVersionCoreAsync(DefaultProjectSnapshot project, DocumentSnapshot document)
            {
                // We only need to produce the generated code if any of our inputs is newer than the
                // previously cached output.
                //
                // First find the versions that are the inputs:
                // - The project + computed state
                // - The imports
                // - This document
                //
                // All of these things are cached, so no work is wasted if we do need to generate the code.
                var configurationVersion         = project.State.ConfigurationVersion;
                var projectWorkspaceStateVersion = project.State.ProjectWorkspaceStateVersion;
                var documentCollectionVersion    = project.State.DocumentCollectionVersion;
                var imports = await GetImportsAsync(project, document).ConfigureAwait(false);

                var documentVersion = await document.GetTextVersionAsync().ConfigureAwait(false);

                // OK now that have the previous output and all of the versions, we can see if anything
                // has changed that would require regenerating the code.
                var inputVersion = documentVersion;

                if (inputVersion.GetNewerVersion(configurationVersion) == configurationVersion)
                {
                    inputVersion = configurationVersion;
                }

                if (inputVersion.GetNewerVersion(projectWorkspaceStateVersion) == projectWorkspaceStateVersion)
                {
                    inputVersion = projectWorkspaceStateVersion;
                }

                if (inputVersion.GetNewerVersion(documentCollectionVersion) == documentCollectionVersion)
                {
                    inputVersion = documentCollectionVersion;
                }

                for (var i = 0; i < imports.Count; i++)
                {
                    var importVersion = imports[i].Version;
                    if (inputVersion.GetNewerVersion(importVersion) == importVersion)
                    {
                        inputVersion = importVersion;
                    }
                }

                RazorCodeDocument olderOutput = null;
                var olderInputVersion         = default(VersionStamp);
                var olderCSharpOutputVersion  = default(VersionStamp);
                var olderHtmlOutputVersion    = default(VersionStamp);

                if (_older?.TaskUnsafeReference != null &&
                    _older.TaskUnsafeReference.TryGetTarget(out var taskUnsafe))
                {
                    (olderOutput, olderInputVersion, olderCSharpOutputVersion, olderHtmlOutputVersion) = await taskUnsafe.ConfigureAwait(false);

                    if (inputVersion.GetNewerVersion(olderInputVersion) == olderInputVersion)
                    {
                        // Nothing has changed, we can use the cached result.
                        lock (_lock)
                        {
                            TaskUnsafeReference = _older.TaskUnsafeReference;
                            _older = null;
                            return(olderOutput, olderInputVersion, olderCSharpOutputVersion, olderHtmlOutputVersion);
                        }
                    }
                }

                // OK we have to generate the code.
                var importSources = new List <RazorSourceDocument>();
                var projectEngine = project.GetProjectEngine();

                foreach (var item in imports)
                {
                    var importProjectItem = item.FilePath == null ? null : projectEngine.FileSystem.GetItem(item.FilePath, item.FileKind);
                    var sourceDocument    = await GetRazorSourceDocumentAsync(item.Document, importProjectItem).ConfigureAwait(false);

                    importSources.Add(sourceDocument);
                }

                var projectItem    = document.FilePath == null ? null : projectEngine.FileSystem.GetItem(document.FilePath, document.FileKind);
                var documentSource = await GetRazorSourceDocumentAsync(document, projectItem).ConfigureAwait(false);


                var codeDocument   = projectEngine.ProcessDesignTime(documentSource, fileKind: document.FileKind, importSources, project.TagHelpers);
                var csharpDocument = codeDocument.GetCSharpDocument();
                var htmlDocument   = codeDocument.GetHtmlDocument();

                // OK now we've generated the code. Let's check if the output is actually different. This is
                // a valuable optimization for our use cases because lots of changes you could make require
                // us to run code generation, but don't change the result.
                //
                // Note that we're talking about the effect on the generated C#/HTML here (not the other artifacts).
                // This is the reason why we have three versions associated with the document.
                //
                // The INPUT version is related the .cshtml files and tag helpers
                // The CSHARPOUTPUT version is related to the generated C#
                // The HTMLOUTPUT version is related to the generated HTML
                //
                // Examples:
                //
                // A change to a tag helper not used by this document - updates the INPUT version, but not
                // the OUTPUT version.
                //
                //
                // Razor IDE features should always retrieve the output and party on it regardless. Depending
                // on the use cases we may or may not need to synchronize the output.

                var outputCSharpVersion = inputVersion;
                var outputHtmlVersion   = inputVersion;

                if (olderOutput != null)
                {
                    if (string.Equals(
                            olderOutput.GetCSharpDocument().GeneratedCode,
                            csharpDocument.GeneratedCode,
                            StringComparison.Ordinal))
                    {
                        outputCSharpVersion = olderCSharpOutputVersion;
                    }

                    if (string.Equals(
                            olderOutput.GetHtmlDocument().GeneratedHtml,
                            htmlDocument.GeneratedHtml,
                            StringComparison.Ordinal))
                    {
                        outputHtmlVersion = olderHtmlOutputVersion;
                    }
                }

                if (document is DefaultDocumentSnapshot defaultDocument)
                {
                    defaultDocument.State.HostDocument.GeneratedDocumentContainer.SetOutput(
                        defaultDocument,
                        csharpDocument,
                        htmlDocument,
                        inputVersion,
                        outputCSharpVersion,
                        outputHtmlVersion);
                }

                return(codeDocument, inputVersion, outputCSharpVersion, outputHtmlVersion);
            }