diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index c5d209fb1fda5..5bd1db04eb95e 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,16 +6,4 @@ FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT} # Set up machine requirements to build the repo RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends cmake llvm-9 clang-9 \ - build-essential python curl git lldb-6.0 liblldb-6.0-dev \ - libunwind8 libunwind8-dev gettext libicu-dev liblttng-ust-dev \ - libssl-dev libnuma-dev libkrb5-dev zlib1g-dev ninja-build - -# Install V8 Engine -SHELL ["/bin/bash", "-c"] - -RUN curl -sSL "https://netcorenativeassets.blob.core.windows.net/resource-packages/external/linux/chromium-v8/v8-linux64-rel-8.5.183.zip" -o ./v8.zip \ - && unzip ./v8.zip -d /usr/local/v8 \ - && echo $'#!/usr/bin/env bash\n\ -"/usr/local/v8/d8" --snapshot_blob="/usr/local/v8/snapshot_blob.bin" "$@"\n' > /usr/local/bin/v8 \ - && chmod +x /usr/local/bin/v8 \ No newline at end of file + && apt-get -y install --no-install-recommends curl git diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md index c152a61951ad2..15ed9a97a5f4f 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md @@ -98,3 +98,11 @@ https://github.com/dotnet/roslyn/issues/57750 public S() { Y = 0; } // ok } ``` + +7. Before Visual Studio 17.2, the C# compiler would accept incorrect default argument values involving a reference conversion of a string constant, and would emit `null` as the constant value instead of the default value specified in source. In Visual Studio 17.2, this becomes an error. See [roslyn#59806](https://github.com/dotnet/roslyn/pull/59806). + + For instance, the following results in an error in 17.2: + ```csharp + void M(IEnumerable s = "hello") + ``` + diff --git a/docs/contributing/Compiler Test Plan.md b/docs/contributing/Compiler Test Plan.md index 2abe570bdef9c..006716142562f 100644 --- a/docs/contributing/Compiler Test Plan.md +++ b/docs/contributing/Compiler Test Plan.md @@ -2,7 +2,8 @@ This document provides guidance for thinking about language interactions and tes # General concerns: - Completeness of the specification as a guide for testing (is the spec complete enough to suggest what the compiler should do in each scenario?) -- Other external documentation +- *Ping* for new breaking changes and general ping for partner teams (Bill, Kathleen, Mads, IDE, Razor) +- Help review external documentation - Backward and forward compatibility (interoperation with previous and future compilers, each in both directions) - Error handling/recovery (missing libraries, including missing types in mscorlib; errors in parsing, ambiguous lookup, inaccessible lookup, wrong kind of thing found, instance vs static thing found, wrong type for the context, value vs variable) - BCL (including mono) and other customer impact diff --git a/eng/Versions.props b/eng/Versions.props index 8b8b2d3a2ebf7..db28578cd2987 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -8,7 +8,7 @@ 4 2 0 - 2 + 3 $(MajorVersion).$(MinorVersion).$(PatchVersion) + + + + + + + + + + + + + + diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/InlineRenameAdornment.xaml.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/InlineRenameAdornment.xaml.cs new file mode 100644 index 0000000000000..9138e5550fa4a --- /dev/null +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/InlineRenameAdornment.xaml.cs @@ -0,0 +1,143 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.CodeAnalysis.Editor.InlineRename.Adornment +{ + /// + /// Interaction logic for InlineRenameAdornment.xaml + /// + internal partial class InlineRenameAdornment : UserControl, IDisposable + { + private readonly InlineRenameAdornmentViewModel _viewModel; + private readonly ITextView _textView; + + public InlineRenameAdornment(InlineRenameAdornmentViewModel viewModel, ITextView textView) + { + DataContext = _viewModel = viewModel; + _textView = textView; + + _textView.LayoutChanged += TextView_LayoutChanged; + _textView.ViewportHeightChanged += TextView_ViewPortChanged; + _textView.ViewportWidthChanged += TextView_ViewPortChanged; + _textView.LostAggregateFocus += TextView_LostFocus; + _textView.Caret.PositionChanged += TextView_CursorChanged; + + // On initialization focus the first tab target + Initialized += (s, e) => MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); + + InitializeComponent(); + PositionAdornment(); + } + +#pragma warning disable CA1822 // Mark members as static - used in xaml + public string RenameOverloads => EditorFeaturesResources.Include_overload_s; + public string SearchInComments => EditorFeaturesResources.Include_comments; + public string SearchInStrings => EditorFeaturesResources.Include_strings; + public string ApplyRename => EditorFeaturesResources.Apply1; + public string CancelRename => EditorFeaturesResources.Cancel; + public string PreviewChanges => EditorFeaturesResources.Preview_changes1; + public string SubmitText => EditorFeaturesWpfResources.Enter_to_rename_shift_enter_to_preview; +#pragma warning restore CA1822 // Mark members as static + + private void TextView_CursorChanged(object sender, CaretPositionChangedEventArgs e) + => _viewModel.Cancel(); + + private void TextView_LostFocus(object sender, EventArgs e) + => _viewModel.Cancel(); + + private void TextView_ViewPortChanged(object sender, EventArgs e) + => PositionAdornment(); + + private void TextView_LayoutChanged(object sender, TextViewLayoutChangedEventArgs e) + => PositionAdornment(); + + private void PositionAdornment() + { + var top = _textView.Caret.Bottom + 5; + var left = _textView.Caret.Left - 5; + + Canvas.SetTop(this, top); + Canvas.SetLeft(this, left); + } + + public void Dispose() + { + _viewModel.Dispose(); + + _textView.LayoutChanged -= TextView_LayoutChanged; + _textView.ViewportHeightChanged -= TextView_ViewPortChanged; + _textView.ViewportWidthChanged -= TextView_ViewPortChanged; + _textView.LostAggregateFocus -= TextView_LostFocus; + _textView.Caret.PositionChanged -= TextView_CursorChanged; + } + + private void Submit_Click(object sender, RoutedEventArgs e) + { + _viewModel.Submit(); + } + + private void Adornment_KeyDown(object sender, KeyEventArgs e) + { + switch (e.Key) + { + case Key.Enter: + e.Handled = true; + _viewModel.Submit(); + break; + + case Key.Escape: + e.Handled = true; + _viewModel.Cancel(); + break; + + case Key.Tab: + // We don't want tab to lose focus for the adornment, so manually + // loop focus back to the first item that is focusable. + FrameworkElement lastItem = _viewModel.IsExpanded + ? FileRenameCheckbox + : IdentifierTextBox; + + if (lastItem.IsFocused) + { + e.Handled = true; + MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); + } + + break; + } + } + + private void IdentifierTextBox_GotFocus(object sender, RoutedEventArgs e) + { + IdentifierTextBox.SelectAll(); + } + + private void Adornment_ConsumeMouseEvent(object sender, MouseButtonEventArgs e) + { + e.Handled = true; + } + + private void Adornment_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) + { + if (e.OldFocus == this) + { + return; + } + + IdentifierTextBox.Focus(); + e.Handled = true; + } + + private void ToggleExpand(object sender, RoutedEventArgs e) + { + _viewModel.IsExpanded = !_viewModel.IsExpanded; + } + } +} diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/InlineRenameAdornmentViewModel.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/InlineRenameAdornmentViewModel.cs new file mode 100644 index 0000000000000..c9932ebf66ef6 --- /dev/null +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/InlineRenameAdornmentViewModel.cs @@ -0,0 +1,269 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Windows.Interop; +using Microsoft.CodeAnalysis.Editor.Implementation.InlineRename; +using Microsoft.CodeAnalysis.InlineRename; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Rename; +using Microsoft.VisualStudio.PlatformUI.OleComponentSupport; + +namespace Microsoft.CodeAnalysis.Editor.InlineRename.Adornment +{ + internal class InlineRenameAdornmentViewModel : INotifyPropertyChanged, IDisposable + { + private readonly InlineRenameSession _session; + private OleComponent? _oleComponent; + private bool _disposedValue; + public event PropertyChangedEventHandler? PropertyChanged; + + public InlineRenameAdornmentViewModel(InlineRenameSession session) + { + _session = session; + _session.ReplacementTextChanged += OnReplacementTextChanged; + + _previewChangesFlag = _session.PreviewChanges; + _renameFileFlag = _session.Options.RenameFile; + _renameInStringsFlag = _session.Options.RenameInStrings; + _renameInCommentsFlag = _session.Options.RenameInComments; + _renameOverloadsFlag = _session.Options.RenameOverloads; + + RegisterOleComponent(); + } + + public string IdentifierText + { + get => _session.ReplacementText; + set + { + if (value != _session.ReplacementText) + { + _session.ApplyReplacementText(value, propagateEditImmediately: false); + NotifyPropertyChanged(nameof(IdentifierText)); + } + } + } + + public bool AllowFileRename => _session.FileRenameInfo == InlineRenameFileRenameInfo.Allowed; + public bool ShowFileRename => _session.FileRenameInfo != InlineRenameFileRenameInfo.NotAllowed; + + public string FileRenameString => _session.FileRenameInfo switch + { + InlineRenameFileRenameInfo.TypeDoesNotMatchFileName => EditorFeaturesResources.Rename_file_name_doesnt_match, + InlineRenameFileRenameInfo.TypeWithMultipleLocations => EditorFeaturesResources.Rename_file_partial_type, + _ => EditorFeaturesResources.Rename_symbols_file + }; + + private bool _renameInCommentsFlag; + public bool RenameInCommentsFlag + { + get => _renameInCommentsFlag; + set + { + if (Set(ref _renameInCommentsFlag, value)) + { + _session.RenameService.GlobalOptions.SetGlobalOption(new OptionKey(InlineRenameSessionOptionsStorage.RenameInComments), value); + _session.RefreshRenameSessionWithOptionsChanged(_session.Options with { RenameInComments = value }); + } + } + } + + private bool _renameInStringsFlag; + public bool RenameInStringsFlag + { + get => _renameInStringsFlag; + set + { + if (Set(ref _renameInStringsFlag, value)) + { + _session.RenameService.GlobalOptions.SetGlobalOption(new OptionKey(InlineRenameSessionOptionsStorage.RenameInStrings), value); + _session.RefreshRenameSessionWithOptionsChanged(_session.Options with { RenameInStrings = value }); + } + } + } + + private bool _renameFileFlag; + public bool RenameFileFlag + { + get => _renameFileFlag; + set + { + if (Set(ref _renameFileFlag, value)) + { + _session.RenameService.GlobalOptions.SetGlobalOption(new OptionKey(InlineRenameSessionOptionsStorage.RenameFile), value); + _session.RefreshRenameSessionWithOptionsChanged(_session.Options with { RenameFile = value }); + } + } + } + + private bool _previewChangesFlag; + public bool PreviewChangesFlag + { + get => _previewChangesFlag; + set + { + if (Set(ref _previewChangesFlag, value)) + { + _session.RenameService.GlobalOptions.SetGlobalOption(new OptionKey(InlineRenameSessionOptionsStorage.PreviewChanges), value); + _session.SetPreviewChanges(value); + } + } + } + + private bool _renameOverloadsFlag; + public bool RenameOverloadsFlag + { + get => _renameOverloadsFlag; + set + { + if (Set(ref _renameOverloadsFlag, value)) + { + _session.RenameService.GlobalOptions.SetGlobalOption(new OptionKey(InlineRenameSessionOptionsStorage.RenameOverloads), value); + _session.RefreshRenameSessionWithOptionsChanged(_session.Options with { RenameOverloads = value }); + } + } + } + + private bool _isCollapsed; + public bool IsCollapsed + { + get => _isCollapsed; + set + { + if (Set(ref _isCollapsed, value)) + { + NotifyPropertyChanged(nameof(IsExpanded)); + } + } + } + + public bool IsExpanded + { + get => !IsCollapsed; + set => IsCollapsed = !value; + } + + public bool IsRenameOverloadsEditable + => !_session.MustRenameOverloads; + + public void Submit() + { + _session.Commit(); + } + + public void Cancel() + { + _session.Cancel(); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// Shell routes commands based on focused tool window. Since we're outside of a tool window, + /// Editor can end up intercepting commands and TYPECHARs sent to us, even when we're focused, + /// so hook in and intercept each message for WPF. + /// + public void RegisterOleComponent() + { + Debug.Assert(_oleComponent is null); + + _oleComponent = OleComponent.CreateHostedComponent("Microsoft CodeAnalysis Inline Rename"); + _oleComponent.PreTranslateMessage += OnPreTranslateMessage; + _oleComponent.BeginTracking(); + } + + private void UnregisterOleComponent() + { + if (_oleComponent is not null) + { + _oleComponent.EndTracking(); + _oleComponent.PreTranslateMessage -= OnPreTranslateMessage; + _oleComponent.Dispose(); + _oleComponent = null; + } + } + + private void OnPreTranslateMessage(object sender, PreTranslateMessageEventArgs e) + { + var msg = e.Message; + if (ComponentDispatcher.RaiseThreadMessage(ref msg) || IsSuppressedMessage(msg)) + { + e.MessageConsumed = true; + } + + // When the adornment is focused, we register an OleComponent to divert window messages + // away from the editor and back to WPF to enable proper handling of arrows, backspace, + // delete, etc. Unfortunately, anything not handled by WPF is then propagated back to the + // shell command system where it is handled by the open editor window. + // To avoid unhandled arrow commands from being handled by editor, + // we mark them as handled so long as the adornment is focused. + static bool IsSuppressedMessage(MSG msg) + => msg.message switch + { + 0x0100 or // WM_KEYDOWN + 0x0101 // WM_KEYUP + => msg.wParam.ToInt32() switch + { + >= 0x0025 and <= 0x0028 => true, // VK_LEFT, VK_UP, VK_RIGHT, and VK_DOWN + + 0x0021 or // VK_PRIOR (Page Up) + 0x0022 or // VK_NEXT (Page Down) + 0x0023 or // VK_END + 0x0024 or // VK_HOME + 0x0D00 or // VK_RETURN + 0x0009 => true, // VK_TAB + + _ => false + }, + + _ => false + }; + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + _session.ReplacementTextChanged -= OnReplacementTextChanged; + + UnregisterOleComponent(); + } + + _disposedValue = true; + } + } + + private void OnReplacementTextChanged(object sender, EventArgs e) + { + NotifyPropertyChanged(nameof(IdentifierText)); + } + + private void NotifyPropertyChanged([CallerMemberName] string? name = null) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); + + private bool Set(ref T field, T newValue, [CallerMemberName] string? name = null) + { + if (EqualityComparer.Default.Equals(field, newValue)) + { + return false; + } + + field = newValue; + NotifyPropertyChanged(name); + return true; + } + } +} diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/Dashboard/Dashboard.xaml b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/Dashboard.xaml similarity index 96% rename from src/EditorFeatures/Core.Wpf/InlineRename/Dashboard/Dashboard.xaml rename to src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/Dashboard.xaml index 18c0b5beaba69..f0a17489c9676 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/Dashboard/Dashboard.xaml +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Dashboard/Dashboard.xaml @@ -24,15 +24,15 @@ - + - +