提交 72eacdff 编写于 作者: J Jason Malinowski

Merge pull request #11260 from jasonmalinowski/implement-document-and-project-specific-options

Correctly format files with per-document formatting options
......@@ -97,7 +97,7 @@ protected override void FormatAndApply(Document document, int position, Cancella
return;
}
var changes = Formatter.GetFormattedTextChanges(root, new TextSpan[] { TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End) }, document.Project.Solution.Workspace, options: null, // use default
var changes = Formatter.GetFormattedTextChanges(root, new TextSpan[] { TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End) }, document.Project.Solution.Workspace, options: document.Options,
rules: null, // use default
cancellationToken: cancellationToken);
......
......@@ -82,7 +82,7 @@ public async Task<IList<TextChange>> GetFormattingChangesAsync(Document document
var span = textSpan.HasValue ? textSpan.Value : new TextSpan(0, root.FullSpan.Length);
var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(root, span);
return Formatter.GetFormattedTextChanges(root, new TextSpan[] { formattingSpan }, document.Project.Solution.Workspace, cancellationToken: cancellationToken);
return Formatter.GetFormattedTextChanges(root, new TextSpan[] { formattingSpan }, document.Project.Solution.Workspace, document.Options, cancellationToken);
}
public async Task<IList<TextChange>> GetFormattingChangesOnPasteAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken)
......@@ -98,7 +98,7 @@ public async Task<IList<TextChange>> GetFormattingChangesOnPasteAsync(Document d
var rules = new List<IFormattingRule>() { new PasteFormattingRule() };
rules.AddRange(service.GetDefaultFormattingRules());
return Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(formattingSpan), document.Project.Solution.Workspace, options: null, rules: rules, cancellationToken: cancellationToken);
return Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(formattingSpan), document.Project.Solution.Workspace, document.Options, rules, cancellationToken);
}
private IEnumerable<IFormattingRule> GetFormattingRules(Document document, int position)
......
......@@ -14,33 +14,6 @@ namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions
{
internal static partial class ITextSnapshotExtensions
{
#if false
/// <summary>
/// get Document corresponding to the snapshot
/// </summary>
public static bool TryGetDocument(this ITextSnapshot snapshot, out Document document)
{
document = snapshot.AsText().GetRelatedDocumentsWithChanges().FirstOrDefault();
return document != null;
}
#endif
/// <summary>
/// format given snapshot and apply text changes to buffer
/// </summary>
public static void FormatAndApplyToBuffer(this ITextSnapshot snapshot, CancellationToken cancellationToken)
{
snapshot.FormatAndApplyToBuffer(new TextSpan(0, snapshot.Length), rules: null, cancellationToken: cancellationToken);
}
/// <summary>
/// format given snapshot and apply text changes to buffer
/// </summary>
public static void FormatAndApplyToBuffer(this ITextSnapshot snapshot, IEnumerable<IFormattingRule> rules, CancellationToken cancellationToken)
{
snapshot.FormatAndApplyToBuffer(new TextSpan(0, snapshot.Length), rules, cancellationToken);
}
/// <summary>
/// format given snapshot and apply text changes to buffer
/// </summary>
......@@ -63,7 +36,7 @@ public static void FormatAndApplyToBuffer(this ITextSnapshot snapshot, TextSpan
rules = GetFormattingRules(document, rules, span);
var root = document.GetSyntaxRootAsync(cancellationToken).WaitAndGetResult(cancellationToken);
var changes = Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(span), document.Project.Solution.Workspace, options: null, rules: rules, cancellationToken: cancellationToken);
var changes = Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(span), document.Project.Solution.Workspace, document.Options, rules, cancellationToken);
using (Logger.LogBlock(FunctionId.Formatting_ApplyResultToBuffer, cancellationToken))
{
......
......@@ -119,7 +119,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit
Return New SimpleCodeCleanupProvider(PredefinedCodeCleanupProviderNames.Format,
Function(doc, spans, c) FormatAsync(doc, spans, rules, c),
Function(r, spans, w, c) Format(r, spans, w, rules, c))
Function(r, spans, w, c) Format(r, spans, w, document.Options, rules, c))
End Function
Private Async Function FormatAsync(document As Document, spans As IEnumerable(Of TextSpan), rules As IEnumerable(Of IFormattingRule), cancellationToken As CancellationToken) As Task(Of Document)
......@@ -128,19 +128,19 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit
If document.TryGetText(oldText) Then
Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
Dim newText = oldText.WithChanges(Formatter.GetFormattedTextChanges(root, spans, document.Project.Solution.Workspace, options:=Nothing, rules:=rules, cancellationToken:=cancellationToken))
Dim newText = oldText.WithChanges(Formatter.GetFormattedTextChanges(root, spans, document.Project.Solution.Workspace, document.Options, rules, cancellationToken))
Return document.WithText(newText)
End If
Return Await Formatter.FormatAsync(document, spans, options:=Nothing, rules:=rules, cancellationToken:=cancellationToken).ConfigureAwait(False)
Return Await Formatter.FormatAsync(document, spans, document.Options, rules, cancellationToken).ConfigureAwait(False)
End Function
Private Function Format(root As SyntaxNode, spans As IEnumerable(Of TextSpan), workspace As Workspace, rules As IEnumerable(Of IFormattingRule), cancellationToken As CancellationToken) As SyntaxNode
Private Function Format(root As SyntaxNode, spans As IEnumerable(Of TextSpan), workspace As Workspace, options As OptionSet, rules As IEnumerable(Of IFormattingRule), cancellationToken As CancellationToken) As SyntaxNode
' if old text already exist, use fast path for formatting
Dim oldText As SourceText = Nothing
If root.SyntaxTree IsNot Nothing AndAlso root.SyntaxTree.TryGetText(oldText) Then
Dim changes = Formatter.GetFormattedTextChanges(root, spans, workspace, options:=Nothing, rules:=rules, cancellationToken:=cancellationToken)
Dim changes = Formatter.GetFormattedTextChanges(root, spans, workspace, options, rules, cancellationToken)
' no change
If changes.Count = 0 Then
......@@ -150,7 +150,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit
Return root.SyntaxTree.WithChangedText(oldText.WithChanges(changes)).GetRoot(cancellationToken)
End If
Return Formatter.Format(root, spans, workspace, options:=Nothing, rules:=rules, cancellationToken:=cancellationToken)
Return Formatter.Format(root, spans, workspace, options, rules, cancellationToken)
End Function
Private Function GetFormattingRules(
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.VisualStudio.LanguageServices.Implementation;
namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService
......@@ -20,15 +18,7 @@ public CSharpEditorFactory(CSharpPackage package)
{
}
protected override string ContentTypeName
{
get { return "CSharp"; }
}
protected override IList<TextChange> GetFormattedTextChanges(VisualStudioWorkspace workspace, string filePath, SourceText text, CancellationToken cancellationToken)
{
var root = SyntaxFactory.ParseSyntaxTree(text, path: filePath, cancellationToken: cancellationToken).GetRoot(cancellationToken);
return Formatter.GetFormattedTextChanges(root, workspace, cancellationToken: cancellationToken);
}
protected override string ContentTypeName => ContentTypeNames.CSharpContentType;
protected override string LanguageName => LanguageNames.CSharp;
}
}
// Copyright (c) Microsoft. 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.IO;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Text.Shared.Extensions;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.Designer.Interfaces;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.Utilities;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation
{
......@@ -64,6 +66,7 @@ protected IComponentModel ComponentModel
}
protected abstract string ContentTypeName { get; }
protected abstract string LanguageName { get; }
public void SetEncoding(bool value)
{
......@@ -252,54 +255,69 @@ int IVsEditorFactoryNotify.NotifyItemRenamed(IVsHierarchy pHier, uint itemid, st
private void FormatDocumentCreatedFromTemplate(IVsHierarchy hierarchy, uint itemid, string filePath, CancellationToken cancellationToken)
{
// A file has been created on disk which the user added from the "Add Item" dialog. The
// project system hasn't told us about this file yet, so we're free to edit it directly
// on disk.
// A file has been created on disk which the user added from the "Add Item" dialog. We need
// to include this in a workspace to figure out the right options it should be formatted with.
// This requires us to place it in the correct project.
var workspace = ComponentModel.GetService<VisualStudioWorkspace>();
var solution = workspace.CurrentSolution;
var contentTypeRegistryService = ComponentModel.GetService<IContentTypeRegistryService>();
var contentType = contentTypeRegistryService.GetContentType(ContentTypeName);
ProjectId projectIdToAddTo = null;
var textDocumentFactoryService = ComponentModel.GetService<ITextDocumentFactoryService>();
using (var textDocument = textDocumentFactoryService.CreateAndLoadTextDocument(filePath, contentType))
foreach (var projectId in solution.ProjectIds)
{
// Some templates will hand us files that aren't CRLF. Let's convert them. This is
// more or less copied from IEditorOperations.NormalizeLineEndings, but we can't use
// it because it assumes we have an actual view
using (var edit = textDocument.TextBuffer.CreateEdit())
if (workspace.GetHierarchy(projectId) == hierarchy)
{
foreach (var line in textDocument.TextBuffer.CurrentSnapshot.Lines)
{
cancellationToken.ThrowIfCancellationRequested();
projectIdToAddTo = projectId;
break;
}
}
// If there is a linebreak at all...
if (line.LineBreakLength != 0)
{
if (line.GetLineBreakText() != "\r\n")
{
edit.Replace(line.End, line.LineBreakLength, "\r\n");
}
}
}
if (projectIdToAddTo == null)
{
// We don't have a project for this, so we'll just make up a fake project altogether
var temporaryProject = solution.AddProject(
name: nameof(FormatDocumentCreatedFromTemplate),
assemblyName: nameof(FormatDocumentCreatedFromTemplate),
language: LanguageName);
solution = temporaryProject.Solution;
projectIdToAddTo = temporaryProject.Id;
}
edit.Apply();
}
var documentId = DocumentId.CreateNewId(projectIdToAddTo);
var forkedSolution = solution.AddDocument(DocumentInfo.Create(documentId, filePath, loader: new FileTextLoader(filePath, defaultEncoding: null), filePath: filePath));
var addedDocument = forkedSolution.GetDocument(documentId);
var formattedTextChanges = GetFormattedTextChanges(ComponentModel.GetService<VisualStudioWorkspace>(), filePath, textDocument.TextBuffer.CurrentSnapshot.AsText(), cancellationToken);
var rootToFormat = addedDocument.GetSyntaxRootAsync(cancellationToken).WaitAndGetResult(cancellationToken);
using (var edit = textDocument.TextBuffer.CreateEdit())
{
foreach (var change in formattedTextChanges)
{
edit.Replace(change.Span.ToSpan(), change.NewText);
}
var formattedTextChanges = Formatter.GetFormattedTextChanges(rootToFormat, workspace, addedDocument.Options, cancellationToken);
var formattedText = addedDocument.GetTextAsync(cancellationToken).WaitAndGetResult(cancellationToken).WithChanges(formattedTextChanges);
edit.Apply();
}
// Ensure the line endings are normalized. The formatter doesn't touch everything if it doesn't need to.
string targetLineEnding = addedDocument.Options.GetOption(FormattingOptions.NewLine);
var originalText = formattedText;
foreach (var originalLine in originalText.Lines)
{
string originalNewLine = originalText.ToString(CodeAnalysis.Text.TextSpan.FromBounds(originalLine.End, originalLine.EndIncludingLineBreak));
textDocument.Save();
// Check if we have a line ending, so we don't go adding one to the end if we don't need to.
if (originalNewLine.Length > 0 && originalNewLine != targetLineEnding)
{
var currentLine = formattedText.Lines[originalLine.LineNumber];
var currentSpan = CodeAnalysis.Text.TextSpan.FromBounds(currentLine.End, currentLine.EndIncludingLineBreak);
formattedText = formattedText.WithChanges(new TextChange(currentSpan, targetLineEnding));
}
}
}
protected abstract IList<TextChange> GetFormattedTextChanges(VisualStudioWorkspace workspace, string filePath, SourceText text, CancellationToken cancellationToken);
IOUtilities.PerformIO(() =>
{
using (var textWriter = new StreamWriter(filePath, append: false, encoding: formattedText.Encoding))
{
// We pass null here for cancellation, since cancelling in the middle of the file write would leave the file corrupted
formattedText.Write(textWriter, cancellationToken: CancellationToken.None);
}
});
}
}
}
......@@ -61,7 +61,7 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella
var rules = ruleFactory.CreateRule(document, start).Concat(Formatter.GetDefaultFormattingRules(document));
// use formatting that return text changes rather than tree rewrite which is more expensive
var originalChanges = Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(adjustedSpan), document.Project.Solution.Workspace, options: null, rules: rules, cancellationToken: cancellationToken);
var originalChanges = Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(adjustedSpan), document.Project.Solution.Workspace, document.Options, rules, cancellationToken);
var originalSpan = RoslynTextSpan.FromBounds(start, end);
var formattedChanges = ruleFactory.FilterFormattedChanges(document, originalSpan, originalChanges);
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor
Imports Microsoft.VisualStudio.LanguageServices.Implementation
Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic
......@@ -18,13 +16,14 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic
Protected Overrides ReadOnly Property ContentTypeName As String
Get
Return "Basic"
Return ContentTypeNames.VisualBasicContentType
End Get
End Property
Protected Overrides Function GetFormattedTextChanges(workspace As VisualStudioWorkspace, filePath As String, text As SourceText, cancellationToken As CancellationToken) As IList(Of TextChange)
Dim root = SyntaxFactory.ParseSyntaxTree(text, path:=filePath, cancellationToken:=cancellationToken).GetRoot(cancellationToken)
Return Formatter.GetFormattedTextChanges(root, workspace, cancellationToken:=cancellationToken)
End Function
Protected Overrides ReadOnly Property LanguageName As String
Get
Return LanguageNames.VisualBasic
End Get
End Property
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册