From c2893148b9c891ce61f89efaeb2dc8ee6ec88df2 Mon Sep 17 00:00:00 2001 From: Navid Date: Tue, 16 May 2023 18:50:15 +0330 Subject: [PATCH 01/20] Add code snippets for C# expression-bodied properties --- snippets/csharp.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/snippets/csharp.json b/snippets/csharp.json index c729dc32c..68eafa797 100644 --- a/snippets/csharp.json +++ b/snippets/csharp.json @@ -354,6 +354,27 @@ ], "description": "An automatically implemented property. C# 3.0 or higher" }, + "propex": { + "prefix": "propex", + "body": [ + "public ${1:int} ${2:MyProperty} => ${3:myVar};", + "$0" + ], + "description": "An expression-bodied property without a backing field. C# 6.0 or higher" + }, + "propexfull": { + "prefix": "propexfull", + "body": [ + "private ${1:int} ${2:myVar};", + "public ${1:int} ${3:MyProperty}", + "{", + "\tget => ${2:myVar};", + "\tset => ${2:myVar} = value;", + "}", + "$0" + ], + "description": "An expression-bodied property with a private field. C# 6.0 or higher" + }, "sim": { "prefix": "sim", "body": [ From abbdadca0a005666be5e6d08c717700f6dce504f Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 24 Jan 2025 16:49:10 +1100 Subject: [PATCH 02/20] Bump Razor --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 31e336bbd..704bce2d6 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "defaults": { "roslyn": "4.14.0-1.25072.1", "omniSharp": "1.39.12", - "razor": "9.0.0-preview.25064.4", + "razor": "9.0.0-preview.25073.1", "razorOmnisharp": "7.0.0-preview.23363.1", "xamlTools": "17.14.35716.216" }, From 92cfe828ec4285711dee492c5f071b26fd2e46b0 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 24 Jan 2025 16:51:13 +1100 Subject: [PATCH 03/20] Update changelog --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb17a97cd..2b94a78d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,12 +46,19 @@ * Update 'use nameof instead of typeof' to support generic types (PR: [#76780](/~https://github.com/dotnet/roslyn/pull/76780)) * Add feature to convert from an explicitly typed lambda to an implicitly typed one. (PR: [#76770](/~https://github.com/dotnet/roslyn/pull/76770)) * Support modifiers with simple lambda parameters. (PR: [#75400](/~https://github.com/dotnet/roslyn/pull/75400)) -* Update Razor to 9.0.0-preview.25064.4 (PR: [#7927](/~https://github.com/dotnet/vscode-csharp/pull/7927)) +* Update Razor to 9.0.0-preview.25073.1 (PR: [#7940](/~https://github.com/dotnet/vscode-csharp/pull/7940)) * Wire up the UseRoslynTokenizer feature properly (#11386) (PR: [#11386](/~https://github.com/dotnet/razor/pull/11386)) * New Razor document formatting engine (#11364) (PR: [#11364](/~https://github.com/dotnet/razor/pull/11364)) * Fix a couple of exceptions encountered when formatting documents with preprocessor directives (#11373) (PR: [#11373](/~https://github.com/dotnet/razor/pull/11373)) * Allow RazorProjectEngine.Process to be cancelled (#11334) (PR: [#11334](/~https://github.com/dotnet/razor/pull/11334)) * Further refactoring of Razor tooling project system (#11320) (PR: [#11320](/~https://github.com/dotnet/razor/pull/11320)) + * Don't create overlapping changes when doing additional formatting (#11413) (PR: [#11413](/~https://github.com/dotnet/razor/pull/11413)) + * Synchronize razor compiler assembly loading (#11394) (PR: [#11394](/~https://github.com/dotnet/razor/pull/11394)) + * Allow generate method to handle delegates (#11402) (PR: [#11402](/~https://github.com/dotnet/razor/pull/11402)) + * Fix bad completion commit in vs code (#11398) (PR: [#11398](/~https://github.com/dotnet/razor/pull/11398)) + * SourceTexts for Everyone! (#11404) (PR: [#11404](/~https://github.com/dotnet/razor/pull/11404)) + * Handful of performance fixes (#11399) (PR: [#11399](/~https://github.com/dotnet/razor/pull/11399)) + * Use the overload that takes an immutable array in serailization (#11393) (PR: [#11393](/~https://github.com/dotnet/razor/pull/11393)) # 2.62.x * Update Roslyn to 4.14.0-1.25060.2 (PR: [#7916](/~https://github.com/dotnet/vscode-csharp/pull/7916)) From 1804c5614f4608020a41ce755890f7a265416b3b Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 27 Jan 2025 10:28:28 -0800 Subject: [PATCH 04/20] Update Roslyn to .NET 9 --- CONTRIBUTING.md | 4 ++-- package.json | 2 +- src/lsptoolshost/dotnetRuntimeExtensionResolver.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b4d1092f1..f505d375a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,7 +73,7 @@ This section shows how to set up local Razor or Roslyn language servers for debu 1. Clone the [Roslyn repository](/~https://github.com/dotnet/roslyn). This repository contains the Roslyn server implementation. 2. Follow the build instructions provided in the repository. -The server DLL is typically at `$roslynRepoRoot/artifacts/bin/Microsoft.CodeAnalysis.LanguageServer/Debug/net8.0/Microsoft.CodeAnalysis.LanguageServer.dll`, but this may vary based on the built configuration. +The server DLL is typically at `$roslynRepoRoot/artifacts/bin/Microsoft.CodeAnalysis.LanguageServer/Debug/net9.0/Microsoft.CodeAnalysis.LanguageServer.dll`, but this may vary based on the built configuration. #### Razor @@ -116,7 +116,7 @@ In your workspace `settings.json` file, add the following lines: ```json "dotnet.server.waitForDebugger": true, -"dotnet.server.path": "/artifacts/bin/Microsoft.CodeAnalysis.LanguageServer/Debug/net8.0/Microsoft.CodeAnalysis.LanguageServer.dll" +"dotnet.server.path": "/artifacts/bin/Microsoft.CodeAnalysis.LanguageServer/Debug/net9.0/Microsoft.CodeAnalysis.LanguageServer.dll" ``` Replace with the actual path to your Roslyn repository. diff --git a/package.json b/package.json index a2308ca2e..d22b0e0ef 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ } }, "defaults": { - "roslyn": "4.14.0-1.25074.7", + "roslyn": "4.14.0-1.25078.4", "omniSharp": "1.39.12", "razor": "9.0.0-preview.25064.4", "razorOmnisharp": "7.0.0-preview.23363.1", diff --git a/src/lsptoolshost/dotnetRuntimeExtensionResolver.ts b/src/lsptoolshost/dotnetRuntimeExtensionResolver.ts index 174e72bc5..a46c94f21 100644 --- a/src/lsptoolshost/dotnetRuntimeExtensionResolver.ts +++ b/src/lsptoolshost/dotnetRuntimeExtensionResolver.ts @@ -14,9 +14,9 @@ import { CSharpExtensionId } from '../constants/csharpExtensionId'; import { readFile } from 'fs/promises'; import { IDotnetAcquireResult, IDotnetFindPathContext } from './dotnetRuntimeExtensionApi'; -const DotNetMajorVersion = '8'; +const DotNetMajorVersion = '9'; const DotNetMinorVersion = '0'; -const DotNetPatchVersion = '10'; +const DotNetPatchVersion = '1'; export const DotNetRuntimeVersion = `${DotNetMajorVersion}.${DotNetMinorVersion}.${DotNetPatchVersion}`; /** From 88555869364f90629c69770d3c6eff5a1bae5449 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Thu, 30 Jan 2025 14:50:39 -0800 Subject: [PATCH 05/20] Remove scheduled insertion run --- azure-pipelines/green-insertion.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/azure-pipelines/green-insertion.yml b/azure-pipelines/green-insertion.yml index 22c3752d3..e07723e7e 100644 --- a/azure-pipelines/green-insertion.yml +++ b/azure-pipelines/green-insertion.yml @@ -1,12 +1,12 @@ trigger: none # We only want to trigger manually or based on resources pr: none -schedules: -- cron: "0 6 * * *" # Daily @ 10 PM PST - displayName: Daily vs-green insertion - branches: - include: - - feature/lsp_tools_host +#schedules: +#- cron: "0 6 * * *" # Daily @ 10 PM PST +# displayName: Daily vs-green insertion +# branches: +# include: +# - feature/lsp_tools_host parameters: - name: InsertTargetBranch From 0161568f187f2c3b49c6ced1effbf32a2ceeeee9 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Thu, 30 Jan 2025 16:28:57 -0800 Subject: [PATCH 06/20] Use .NET8 and .NET9 for CI tests --- azure-pipelines.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bcc565860..aa44840e9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -45,7 +45,7 @@ stages: dotnetVersion: $(defaultDotnetVersion) - stage: - displayName: Test Linux (.NET 6) + displayName: Test Linux (.NET 8) dependsOn: [] jobs: - template: azure-pipelines/test-matrix.yml @@ -57,10 +57,10 @@ stages: pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open - containerName: mcr.microsoft.com/dotnet/sdk:6.0 + containerName: mcr.microsoft.com/dotnet/sdk:8.0 - stage: - displayName: Test Linux (.NET 8) + displayName: Test Linux (.NET 9) dependsOn: [] jobs: - template: azure-pipelines/test-matrix.yml @@ -72,7 +72,7 @@ stages: pool: name: NetCore-Public demands: ImageOverride -equals 1es-ubuntu-2004-open - containerName: mcr.microsoft.com/dotnet/sdk:8.0 + containerName: mcr.microsoft.com/dotnet/sdk:9.0 - stage: Test_Windows_Stage displayName: Test Windows From fd5d4baa180cb27d709fadf2712fe9abe68a8e18 Mon Sep 17 00:00:00 2001 From: dibarbet Date: Sat, 1 Feb 2025 02:47:25 +0000 Subject: [PATCH 07/20] Update main version --- CHANGELOG.md | 2 ++ version.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 425a64bcf..9b17458f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ - Diagnostics related feature requests and improvements [#5951](/~https://github.com/dotnet/vscode-csharp/issues/5951) - Debug from .csproj and .sln [#5876](/~https://github.com/dotnet/vscode-csharp/issues/5876) +# 2.65.x + # 2.64.x * Bump xamlTools to 17.14.35730.156 (PR: [#7932](/~https://github.com/dotnet/vscode-csharp/pull/7941)) diff --git a/version.json b/version.json index 75cb2cde0..364125d35 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "2.64", + "version": "2.65", "publicReleaseRefSpec": [ "^refs/heads/release$", "^refs/heads/prerelease$", From 65fb190c315609fde058ad25e52cfee31e260f5e Mon Sep 17 00:00:00 2001 From: dotnet-bot Date: Sat, 1 Feb 2025 03:21:50 +0000 Subject: [PATCH 08/20] Localization result of 57ae485d87ac1319e0802c6af241157091cc9ee4. --- package.nls.cs.json | 2 +- package.nls.de.json | 2 +- package.nls.es.json | 2 +- package.nls.fr.json | 2 +- package.nls.it.json | 2 +- package.nls.ja.json | 2 +- package.nls.ko.json | 2 +- package.nls.pl.json | 2 +- package.nls.pt-br.json | 2 +- package.nls.ru.json | 2 +- package.nls.tr.json | 2 +- package.nls.zh-cn.json | 2 +- package.nls.zh-tw.json | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.nls.cs.json b/package.nls.cs.json index 141d5028e..54ff647f7 100644 --- a/package.nls.cs.json +++ b/package.nls.cs.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "Automaticky zobrazovat seznam dokončení v seznamech argumentů", "configuration.dotnet.defaultSolution.description": "Cesta výchozího řešení, které se má otevřít v pracovním prostoru. Můžete přeskočit nastavením na „zakázat“. (Dříve omnisharp.defaultLaunchSolution)", "configuration.dotnet.enableXamlTools": "Povolí nástroje XAML při použití sady C# Dev Kit", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "Určuje, zda mají být během formátování dokumentu seskupeny a seřazeny direktivy using. (dříve omnisharp.organizeImportsOnFormat)", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "Zvýrazněte související komponenty JSON pod kurzorem.", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "Zvýraznit související komponenty regulárního výrazu pod kurzorem.", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "Zobrazit nápovědy pro literály", diff --git a/package.nls.de.json b/package.nls.de.json index 3573651f4..ac7da7329 100644 --- a/package.nls.de.json +++ b/package.nls.de.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "Vervollständigungsliste in Argumentlisten automatisch anzeigen", "configuration.dotnet.defaultSolution.description": "Der Pfad der Standardlösung, die im Arbeitsbereich geöffnet werden soll, oder auf \"deaktivieren\" festlegen, um sie zu überspringen. (Zuvor \"omnisharp.defaultLaunchSolution\")", "configuration.dotnet.enableXamlTools": "Aktiviert XAML-Tools bei Verwendung des C#-Dev Kit", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "Gibt an, ob „using“-Anweisungen während der Dokumentformatierung gruppiert und sortiert werden sollen. (Zuvor „omnisharp.organizeImportsOnFormat“)", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "Zugehörige JSON-Komponenten unter dem Cursor markieren.", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "Zugehörige Komponenten regulärer Ausdrücke unter dem Cursor markieren.", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "Hinweise für Literale anzeigen", diff --git a/package.nls.es.json b/package.nls.es.json index ebf08dcbc..3fd96c661 100644 --- a/package.nls.es.json +++ b/package.nls.es.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "Mostrar automáticamente la lista de finalización en las listas de argumentos", "configuration.dotnet.defaultSolution.description": "Ruta de acceso de la solución predeterminada que se va a abrir en el área de trabajo o se establece en \"deshabilitar\" para omitirla. (Anteriormente \"omnisharp.defaultLaunchSolution\")", "configuration.dotnet.enableXamlTools": "Habilita las herramientas XAML al usar el Kit de desarrollo de C#", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "Especifica si las directivas \"using\" deben agruparse y ordenarse durante el formato del documento. (Anteriormente \"omnisharp.organizeImportsOnFormat\")", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "Resaltar los componentes JSON relacionados bajo el cursor.", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "Resaltar los componentes de expresiones regulares relacionados bajo el cursor.", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "Mostrar sugerencias para los literales", diff --git a/package.nls.fr.json b/package.nls.fr.json index 8e4530982..7d63fe08b 100644 --- a/package.nls.fr.json +++ b/package.nls.fr.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "Afficher automatiquement la liste de complétion dans les listes d'arguments", "configuration.dotnet.defaultSolution.description": "Le chemin d’accès de la solution par défaut à ouvrir dans l’espace de travail, ou la valeur ’disable’ pour l’ignorer. (Précédemment `omnisharp.defaultLaunchSolution`)", "configuration.dotnet.enableXamlTools": "Active les outils XAML lors de l’utilisation du Kit de développement C#", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "Spécifie si les directives « using » doivent être regroupées et triées lors de la mise en forme d’un document. (Anciennement `omnisharp.organizeImportsOnFormat`)", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "Mettez en surbrillance les composants JSON associés sous le curseur.", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "Mettre en surbrillance les composants d’expression régulière associés sous le curseur.", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "Afficher les indicateurs pour les littéraux", diff --git a/package.nls.it.json b/package.nls.it.json index b9b6c269f..315958683 100644 --- a/package.nls.it.json +++ b/package.nls.it.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "Mostra automaticamente l'elenco di completamento negli elenchi di argomenti", "configuration.dotnet.defaultSolution.description": "Percorso della soluzione predefinita da aprire nell'area di lavoro o impostare su 'disabilita' per ignorarla. (In precedenza “omnisharp.defaultLaunchSolution”)", "configuration.dotnet.enableXamlTools": "Abilita gli strumenti XAML quando si usa il kit di sviluppo C#", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifica se le direttive \"using\" devono essere raggruppate e ordinate durante la formattazione del documento. (In precedenza `omnisharp.organizeImportsOnFormat`)", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "Evidenziare i componenti JSON correlati sotto il cursore.", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "Evidenzia i componenti dell'espressione regolare correlati sotto il cursore.", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "Mostra suggerimenti per i valori letterali", diff --git a/package.nls.ja.json b/package.nls.ja.json index 90bf3c09f..adebfec10 100644 --- a/package.nls.ja.json +++ b/package.nls.ja.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "引数リストに入力候補一覧を自動的に表示する", "configuration.dotnet.defaultSolution.description": "ワークスペースで開く既定のソリューションのパス。スキップするには 'disable' に設定します。(以前の `omnisharp.defaultLaunchSolution`)", "configuration.dotnet.enableXamlTools": "C# 開発キットを使用するときに XAML ツールを有効にします", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "ドキュメントの書式設定中に 'using' ディレクティブをグループ化して並べ替える必要があるかどうかを指定します。(以前の 'omnisharp.organizeImportsOnFormat')", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "カーソルの下にある関連する JSON コンポーネントをハイライトします。", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "カーソルの下にある関連する正規表現コンポーネントをハイライトします。", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "リテラルのヒントを表示する", diff --git a/package.nls.ko.json b/package.nls.ko.json index 65140aca8..04414b99f 100644 --- a/package.nls.ko.json +++ b/package.nls.ko.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "인수 목록에 자동으로 완성 목록 표시", "configuration.dotnet.defaultSolution.description": "작업 영역에서 열릴 기본 솔루션의 경로, 건너뛰려면 '비활성화'로 설정하세요(이전 `omnisharp.defaultLaunchSolution`).", "configuration.dotnet.enableXamlTools": "C# 개발자 키트를 사용할 때 XAML 도구 사용", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "문서 서식을 지정하는 동안 'using' 지시문을 그룹화하고 정렬할지 여부를 지정합니다. (Previously `omnisharp.organizeImportsOnFormat`)", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "커서 아래에서 관련 JSON 구성 요소를 강조 표시합니다.", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "커서 아래의 관련 정규식 구성 요소를 강조 표시합니다.", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "리터럴에 대한 힌트 표시", diff --git a/package.nls.pl.json b/package.nls.pl.json index 6e7137482..0bf5b7e66 100644 --- a/package.nls.pl.json +++ b/package.nls.pl.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "Automatycznie pokaż listę uzupełniania na listach argumentów", "configuration.dotnet.defaultSolution.description": "Ścieżka domyślnego rozwiązania, która ma zostać otwarta w obszarze roboczym, lub ustawiona na wartość „wyłącz”, aby je pominąć. (Poprzednio „omnisharp.defaultLaunchSolution”)", "configuration.dotnet.enableXamlTools": "Włącza narzędzia XAML podczas korzystania z zestawu deweloperskiego języka C#", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "Określa, czy dyrektywy „using” mają być grupowane i sortowane podczas formatowania dokumentu. (Poprzednio „omnisharp.organizeImportsOnFormat”)", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "Wyróżnij powiązane składniki JSON pod kursorem.", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "Wyróżnij powiązane składniki wyrażenia regularnego pod kursorem.", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "Pokaż wskazówki dla literałów", diff --git a/package.nls.pt-br.json b/package.nls.pt-br.json index 7234ed931..b4da580dc 100644 --- a/package.nls.pt-br.json +++ b/package.nls.pt-br.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "Mostrar automaticamente a lista de conclusão nas listas de argumentos", "configuration.dotnet.defaultSolution.description": "O caminho da solução padrão a ser aberta no workspace ou definido como 'desabilitado' para ignorá-la. (Anteriormente `omnisharp.defaultLaunchSolution`)", "configuration.dotnet.enableXamlTools": "Habilita ferramentas XAML ao usar o Kit de Desenvolvimento em C#", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "Especifica se as diretivas 'usando' devem ser agrupadas e classificadas durante a formatação do documento. (Anteriormente `omnisharp.organizeImportsOnFormat`)", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "Destaque os componentes JSON relacionados sob o cursor.", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "Destaque os componentes de expressão regular relacionados sob o cursor.", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "Mostrar as dicas para os literais", diff --git a/package.nls.ru.json b/package.nls.ru.json index c4af3a4fc..394e74d1b 100644 --- a/package.nls.ru.json +++ b/package.nls.ru.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "Автоматически показывать список завершения в списках аргументов", "configuration.dotnet.defaultSolution.description": "Путь к решению по умолчанию, которое будет открыто в рабочей области. Или задайте значение \"Отключить\", чтобы пропустить его. (Ранее — \"omnisharp.defaultLaunchSolution\")", "configuration.dotnet.enableXamlTools": "Включает инструменты XAML при использовании комплекта разработки C# Dev Kit.", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "Указывает, следует ли группировать и сортировать директивы \"using\" во время форматирования документов. (Ранее — \"omnisharp.organizeImportsOnFormat\")", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "Выделить связанные компоненты JSON под курсором.", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "Выделение связанных компонентов регулярных выражений под курсором.", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "Отображать подсказки для литералов", diff --git a/package.nls.tr.json b/package.nls.tr.json index 03d4f8a4f..d3465c2f4 100644 --- a/package.nls.tr.json +++ b/package.nls.tr.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "Bağımsız değişken listelerinde tamamlama listesini otomatik olarak göster", "configuration.dotnet.defaultSolution.description": "Varsayılan çözümün yolu, çalışma alanında açılacak veya atlamak için 'devre dışı' olarak ayarlanacak. (Daha önce 'omnisharp.defaultLaunchSolution')", "configuration.dotnet.enableXamlTools": "C# Geliştirme Setini kullanırken XAML araçlarını etkinleştirir", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "'using' yönergelerinin belge biçimlendirmesi sırasında gruplandırılarak sıralanıp sıralanmayacağını belirtir. (Önceki adıyla `omnisharp.organizeImportsOnFormat`)", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "İmlecin altındaki ilgili JSON bileşenlerini vurgula.", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "İmleç altındaki ilgili normal ifade bileşenlerini vurgula.", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "Sabit değerler için ipuçlarını göster", diff --git a/package.nls.zh-cn.json b/package.nls.zh-cn.json index a898a9e86..25b33b4e4 100644 --- a/package.nls.zh-cn.json +++ b/package.nls.zh-cn.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "自动显示参数列表中的补全列表", "configuration.dotnet.defaultSolution.description": "要在工作区中打开的默认解决方案的路径,或者设置为“禁用”以跳过它。(之前为 \"omnisharp.defaultLaunchSolution\")", "configuration.dotnet.enableXamlTools": "使用 C# 开发工具包时启用 XAML 工具", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "指定在设置文档格式期间是否应对 “using” 指令进行分组和排序。(以前为 `omnisharp.organizeImportsOnFormat`)", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "突出显示光标下的相关 JSON 组件。", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "突出显示光标下的相关正则表达式组件。", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "显示文本提示", diff --git a/package.nls.zh-tw.json b/package.nls.zh-tw.json index 55edc8b0d..e06f1b29a 100644 --- a/package.nls.zh-tw.json +++ b/package.nls.zh-tw.json @@ -45,7 +45,7 @@ "configuration.dotnet.completion.triggerCompletionInArgumentLists": "自動在引數清單中顯示自動完成清單", "configuration.dotnet.defaultSolution.description": "要在工作區中開啟的預設解決方案路徑,或設為 [停用] 以略過它。(先前為 `omnisharp.defaultLaunchSolution`)", "configuration.dotnet.enableXamlTools": "使用 C# 開發套件時啟用 XAML 工具", - "configuration.dotnet.formatting.organizeImportsOnFormat": "Specifies whether 'using' directives should be grouped and sorted during document formatting. (Previously `omnisharp.organizeImportsOnFormat`)", + "configuration.dotnet.formatting.organizeImportsOnFormat": "指定在文件格式化期間,是否應該將 'using' 指示詞分組和排序。(先前為 'omnisharp.organizeImportsOnFormat')", "configuration.dotnet.highlighting.highlightRelatedJsonComponents": "反白資料指標下的相關 JSON 元件。", "configuration.dotnet.highlighting.highlightRelatedRegexComponents": "反白資料指標下的相關規則運算式元件。", "configuration.dotnet.inlayHints.enableInlayHintsForLiteralParameters": "顯示常值的提示", From 0b06f0affeed1068f7c99c707e74ed5813dfaf7b Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 3 Feb 2025 12:26:44 -0800 Subject: [PATCH 09/20] Add placeholder profiling yaml for pipeline creation --- azure-pipelines/profiling.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 azure-pipelines/profiling.yml diff --git a/azure-pipelines/profiling.yml b/azure-pipelines/profiling.yml new file mode 100644 index 000000000..336cc87e9 --- /dev/null +++ b/azure-pipelines/profiling.yml @@ -0,0 +1,28 @@ +trigger: none +pr: none +schedules: +- cron: '18 1 * * *' # every day at 1:18 AM + displayName: Periodic profiling run + branches: + include: + - main + +variables: +- name: TRACE_DROP_LOCATION + value: $(Build.ArtifactStagingDirectory)/traces/ +- name: MERGED_TRACE_LOCATION + value: $(Build.ArtifactStagingDirectory)/mergedtrace/ + +extends: + template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates + parameters: + pool: + name: AzurePipelines-EO + image: 1ESPT-Ubuntu20.04 + os: Linux + stages: + - stage: run + jobs: + - job: run + steps: + - pwsh: echo "Profiling" From 84295f3538284ff53350af9f8d09c1a0c7fe8854 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Mon, 3 Feb 2025 12:22:07 -0800 Subject: [PATCH 10/20] Add profiling pipeline --- azure-pipelines-official.yml | 2 +- azure-pipelines.yml | 3 +- azure-pipelines/dotnet-variables.yml | 3 + azure-pipelines/profiling.yml | 49 ++++++-- azure-pipelines/test.yml | 20 +++- gulpfile.ts | 1 + package.json | 1 + src/lsptoolshost/profiling.ts | 26 +++++ src/lsptoolshost/roslynLanguageServer.ts | 10 +- tasks/profilingTasks.ts | 112 ++++++++++++++++++ tasks/testHelpers.ts | 141 +++++++++++++++++++++++ tasks/testTasks.ts | 138 ++-------------------- 12 files changed, 358 insertions(+), 148 deletions(-) create mode 100644 azure-pipelines/dotnet-variables.yml create mode 100644 src/lsptoolshost/profiling.ts create mode 100644 tasks/profilingTasks.ts create mode 100644 tasks/testHelpers.ts diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index d4ed8f245..bea03e0af 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -28,7 +28,7 @@ parameters: default: auto variables: - defaultDotnetVersion: '8.0.403' +- template: /azure-pipelines/dotnet-variables.yml@self resources: repositories: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index bcc565860..ec756668a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -29,8 +29,7 @@ schedules: - main variables: -- name: defaultDotnetVersion - value: '8.0.403' +- template: /azure-pipelines/dotnet-variables.yml@self - name: testVSCodeVersion ${{ if eq( variables['Build.Reason'], 'Schedule' ) }}: value: insiders diff --git a/azure-pipelines/dotnet-variables.yml b/azure-pipelines/dotnet-variables.yml new file mode 100644 index 000000000..9381ea25d --- /dev/null +++ b/azure-pipelines/dotnet-variables.yml @@ -0,0 +1,3 @@ +variables: +- name: defaultDotnetVersion + value: '8.0.403' \ No newline at end of file diff --git a/azure-pipelines/profiling.yml b/azure-pipelines/profiling.yml index 336cc87e9..7171b16e3 100644 --- a/azure-pipelines/profiling.yml +++ b/azure-pipelines/profiling.yml @@ -9,20 +9,51 @@ schedules: variables: - name: TRACE_DROP_LOCATION - value: $(Build.ArtifactStagingDirectory)/traces/ + value: $(Build.SourcesDirectory)/out/profiling/ - name: MERGED_TRACE_LOCATION - value: $(Build.ArtifactStagingDirectory)/mergedtrace/ + value: $(Build.SourcesDirectory)/out/profiling/merged/ +- name: LOGS_DIRECTORY + value: $(Build.SourcesDirectory)/out/logs/ +- template: /azure-pipelines/dotnet-variables.yml@self + +resources: + repositories: + - repository: 1ESPipelineTemplates + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release extends: template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates parameters: - pool: - name: AzurePipelines-EO - image: 1ESPT-Ubuntu20.04 - os: Linux + sdl: + sourceAnalysisPool: + name: netcore1espool-internal + image: 1es-windows-2022 + os: windows stages: - - stage: run + - stage: Run_Profiling + displayName: Run Profiling jobs: - - job: run + - job: Profile + pool: + name: netcore1espool-internal + image: 1es-ubuntu-2204 + os: linux + templateContext: + outputs: + - output: pipelineArtifact + targetPath: $(TRACE_DROP_LOCATION) + artifactName: traces + displayName: 📢 Publish intermediate trace files + condition: failed() + - output: pipelineArtifact + targetPath: $(MERGED_TRACE_LOCATION) + artifactName: merged mibc + displayName: 📢 Publish merged MIBC steps: - - pwsh: echo "Profiling" + - template: /azure-pipelines/test.yml@self + parameters: + dotnetVersion: $(defaultDotnetVersion) + npmCommand: profiling + testVSCodeVersion: stable \ No newline at end of file diff --git a/azure-pipelines/test.yml b/azure-pipelines/test.yml index 5f8cb471a..6df830192 100644 --- a/azure-pipelines/test.yml +++ b/azure-pipelines/test.yml @@ -44,9 +44,17 @@ steps: mergeTestResults: true testRunTitle: $(System.StageDisplayName) $(Agent.JobName) (Attempt $(System.JobAttempt)) -- task: PublishPipelineArtifact@1 - condition: failed() - displayName: 'Upload integration test logs' - inputs: - targetPath: '$(Build.SourcesDirectory)/out/logs' - artifactName: 'Test Logs ($(System.StageDisplayName)-$(Agent.JobName)-$(System.JobAttempt))' +- ${{ if eq(variables['System.TeamProject'], 'internal') }}: + - task: 1ES.PublishPipelineArtifact@1 + condition: failed() + displayName: 'Upload integration test logs' + inputs: + path: '$(Build.SourcesDirectory)/out/logs' + artifact: 'Test Logs ($(System.StageDisplayName)-$(Agent.JobName)-$(System.JobAttempt))' +- ${{ else }}: + - task: PublishPipelineArtifact@1 + condition: failed() + displayName: 'Upload integration test logs' + inputs: + targetPath: '$(Build.SourcesDirectory)/out/logs' + artifactName: 'Test Logs ($(System.StageDisplayName)-$(Agent.JobName)-$(System.JobAttempt))' diff --git a/gulpfile.ts b/gulpfile.ts index c75eb2a77..ef52553ce 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -11,3 +11,4 @@ require('./tasks/createTagsTasks'); require('./tasks/debuggerTasks'); require('./tasks/snapTasks'); require('./tasks/signingTasks'); +require('./tasks/profilingTasks'); diff --git a/package.json b/package.json index 310631904..a7db868dd 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "test:integration:devkit": "tsc -p ./ && gulp test:integration:devkit", "test:razor": "tsc -p ./ && npm run compile:razorTextMate && gulp test:razor", "test:razorintegration": "tsc -p ./ && gulp test:razorintegration", + "profiling": "tsc -p ./ && gulp profiling", "test:artifacts": "tsc -p ./ && gulp test:artifacts", "omnisharptest": "tsc -p ./ && gulp omnisharptest", "omnisharptest:unit": "tsc -p ./ && gulp omnisharptest:unit", diff --git a/src/lsptoolshost/profiling.ts b/src/lsptoolshost/profiling.ts new file mode 100644 index 000000000..dd5ac0ff4 --- /dev/null +++ b/src/lsptoolshost/profiling.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { EOL } from 'os'; +import { LogOutputChannel } from 'vscode'; + +export function getProfilingEnvVars(outputChannel: LogOutputChannel): NodeJS.ProcessEnv { + let profilingEnvVars = {}; + if (process.env.ROSLYN_DOTNET_EventPipeOutputPath) { + profilingEnvVars = { + DOTNET_EnableEventPipe: 1, + DOTNET_EventPipeConfig: 'Microsoft-Windows-DotNETRuntime:0x1F000080018:5', + DOTNET_EventPipeOutputPath: process.env.ROSLYN_DOTNET_EventPipeOutputPath, + DOTNET_ReadyToRun: 0, + DOTNET_TieredCompilation: 1, + DOTNET_TC_CallCounting: 0, + DOTNET_TC_QuickJitForLoops: 1, + DOTNET_JitCollect64BitCounts: 1, + }; + outputChannel.trace(`Profiling enabled with:${EOL}${JSON.stringify(profilingEnvVars)}`); + } + + return profilingEnvVars; +} diff --git a/src/lsptoolshost/roslynLanguageServer.ts b/src/lsptoolshost/roslynLanguageServer.ts index 9ab84c1b2..5345cae6b 100644 --- a/src/lsptoolshost/roslynLanguageServer.ts +++ b/src/lsptoolshost/roslynLanguageServer.ts @@ -79,6 +79,7 @@ import { registerSourceGeneratedFilesContentProvider } from './sourceGeneratedFi import { registerMiscellaneousFileNotifier } from './miscellaneousFileNotifier'; import { TelemetryEventNames } from '../shared/telemetryEventNames'; import { RazorDynamicFileChangedParams } from '../razor/src/dynamicFile/dynamicFileUpdatedParams'; +import { getProfilingEnvVars } from './profiling'; let _channel: vscode.LogOutputChannel; let _traceChannel: vscode.OutputChannel; @@ -664,18 +665,23 @@ export class RoslynLanguageServer { args.push('--extensionLogDirectory', context.logUri.fsPath); - const env = dotnetInfo.env; + let env = dotnetInfo.env; if (!languageServerOptions.useServerGC) { // The server by default uses serverGC, if the user opts out we need to set the environment variable to disable it. env.DOTNET_gcServer = '0'; _channel.debug('ServerGC disabled'); } + const profilingEnvVars = getProfilingEnvVars(_channel); + env = { ...env, ...profilingEnvVars }; + + _channel.trace(`Profiling environment variables: ${JSON.stringify(profilingEnvVars)}`); + let childProcess: cp.ChildProcessWithoutNullStreams; const cpOptions: cp.SpawnOptionsWithoutStdio = { detached: true, windowsHide: true, - env: dotnetInfo.env, + env: env, }; if (serverPath.endsWith('.dll')) { diff --git a/tasks/profilingTasks.ts b/tasks/profilingTasks.ts new file mode 100644 index 000000000..ea6fc9133 --- /dev/null +++ b/tasks/profilingTasks.ts @@ -0,0 +1,112 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import fs from 'fs'; +import * as gulp from 'gulp'; +import * as path from 'path'; +import { basicSlnTestProject, runIntegrationTest } from './testHelpers'; +import { outPath } from './projectPaths'; +import { execSync } from 'child_process'; + +createProfilingTasks(); + +function createProfilingTasks() { + const profilingOutputFolder = path.join(outPath, 'profiling'); + gulp.task(`profiling:csharp:${basicSlnTestProject}`, async () => { + // Ensure the profiling output folder exists, otherwise the outputs will not get written. + fs.mkdirSync(path.join(outPath, 'profiling'), { recursive: true }); + + await runIntegrationTest( + basicSlnTestProject, + path.join('lsptoolshost', 'integrationTests'), + `[C#][${basicSlnTestProject}]`, + undefined, + undefined, + { + ROSLYN_DOTNET_EventPipeOutputPath: path.join(profilingOutputFolder, 'csharp-trace.{pid}.nettrace'), + } + ); + + const files = fs.readdirSync(profilingOutputFolder); + const nettraceFiles = files.filter((f) => f.endsWith('.nettrace')); + if (nettraceFiles.length === 0) { + throw new Error('No .nettrace files found in the profiling output folder.'); + } + }); + + gulp.task('mergeTraces', async () => { + await mergeTraces(profilingOutputFolder); + }); + + gulp.task('profiling', gulp.series(`profiling:csharp:${basicSlnTestProject}`, 'mergeTraces')); +} + +async function mergeTraces(profilingOutputFolder: string) { + const files = fs.readdirSync(profilingOutputFolder); + const nettraceFiles = files.filter((f) => f.endsWith('.nettrace')); + if (nettraceFiles.length === 0) { + throw new Error('No .nettrace files found in the profiling output folder.'); + } + + // Function to spawn a tool + function spawnTool(command: string, args: string[], warnOnError = false) { + try { + console.log(`##[command] ${command} ${args.join(' ')}`); + execSync(`${command} ${args.join(' ')}`, { stdio: 'inherit' }); + } catch (error) { + if (warnOnError) { + console.warn(`Failed: ${error}`); + } else { + throw error; + } + } + } + + // Ensure the dotnet-pgo tool is installed. + // Additional versions of this can be found at https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet8-transport/NuGet/dotnet-pgo/ + spawnTool('dotnet', [ + 'tool', + 'update', + '-g', + 'dotnet-pgo', + '--version', + '8.0.0-rc.2.23479.6', + '--add-source', + 'https://pkgs.dev.azure.com/azure-public/vside/_packaging/msft_consumption/nuget/v3/index.json', + '--ignore-failed-sources', + ]); + + console.log('##[group] Converting .nettrace to .mibc'); + nettraceFiles.forEach((file) => { + spawnTool( + 'dotnet-pgo', + [ + 'create-mibc', + '-t', + path.join(profilingOutputFolder, file), + '-o', + path.join(profilingOutputFolder, `${path.basename(file, '.nettrace')}.mibc`), + ], + true + ); + }); + console.log('##[endgroup]'); + + const mibcFiles = fs.readdirSync(profilingOutputFolder).filter((f) => f.endsWith('.mibc')); + if (mibcFiles.length === 0) { + throw new Error('No .mibc files were produced.'); + } + + const mergedTraceLocation = path.join(profilingOutputFolder, 'merged'); + fs.mkdirSync(mergedTraceLocation, { recursive: true }); + + const inputArgs = ['merge', '--compressed']; + mibcFiles.forEach((file) => { + inputArgs.push('-i', path.join(profilingOutputFolder, file)); + }); + inputArgs.push('-o', path.join(mergedTraceLocation, 'merged.mibc')); + + spawnTool('dotnet-pgo', inputArgs); +} diff --git a/tasks/testHelpers.ts b/tasks/testHelpers.ts new file mode 100644 index 000000000..d8dfa5cbf --- /dev/null +++ b/tasks/testHelpers.ts @@ -0,0 +1,141 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import fs from 'fs'; +import * as path from 'path'; +import { rootPath, outPath } from './projectPaths'; +import { prepareVSCodeAndExecuteTests } from '../test/vscodeLauncher'; + +export const basicSlnTestProject = 'slnWithCsproj'; +export const integrationTestProjects = [basicSlnTestProject]; + +export async function runDevKitIntegrationTests( + testAssetName: string, + testFolderName: string, + suiteName: string, + env: NodeJS.ProcessEnv = {} +) { + // Tests using C# Dev Kit tests are a bit different from the rest - we are not able to restart the Dev Kit server and there + // are not easy APIs to use to know if the project is reloading due to workspace changes. + // So we have to isolate the C# Dev Kit tests into smaller test runs (in this case, per file), where each run + // launches VSCode and runs the tests in that file. + const testFolder = path.join(rootPath, 'test', testFolderName); + console.log(`Searching for test files in ${testFolder}`); + const allFiles = fs + .readdirSync(testFolder, { + recursive: true, + }) + .filter((f) => typeof f === 'string'); + const devKitTestFiles = allFiles.filter((f) => f.endsWith('.test.ts')).map((f) => path.join(testFolder, f)); + if (devKitTestFiles.length === 0) { + throw new Error(`No test files found in ${testFolder}`); + } + + let failed: boolean = false; + for (const testFile of devKitTestFiles) { + try { + await runIntegrationTest( + testAssetName, + testFolderName, + suiteName, + `devkit_${testAssetName}.code-workspace`, + testFile, + env + ); + } catch (err) { + // We have to catch the error to continue running tests from the rest of the files. + console.error(`##[error] Tests in ${path.basename(testFile)} failed`, err); + failed = true; + } + } + + if (failed) { + // Ensure the task fails if any tests failed. + throw new Error(`One or more tests failed`); + } +} + +export async function runIntegrationTest( + testAssetName: string, + testFolderName: string, + suiteName: string, + vscodeWorkspaceFileName = `${testAssetName}.code-workspace`, + testFile: string | undefined = undefined, + env: NodeJS.ProcessEnv = {} +): Promise { + const testFolder = path.join('test', testFolderName); + return await runJestIntegrationTest(testAssetName, testFolder, vscodeWorkspaceFileName, suiteName, env, testFile); +} + +/** + * Runs jest based integration tests. + * @param testAssetName the name of the test asset + * @param testFolderName the relative path (from workspace root) + * @param workspaceFileName the name of the vscode workspace file to use. + * @param suiteName a unique name for the test suite being run. + * @param env any environment variables needed. + * @param testFile the full path to a specific test file to run. + */ +export async function runJestIntegrationTest( + testAssetName: string, + testFolderName: string, + workspaceFileName: string, + suiteName: string, + env: NodeJS.ProcessEnv = {}, + testFile: string | undefined = undefined +) { + const logName = testFile ? `${suiteName}_${path.basename(testFile)}` : suiteName; + + // Set VSCode to produce logs in a unique directory for this test run. + const userDataDir = path.join(outPath, 'userData', logName); + + // Test assets are always in a testAssets folder inside the integration test folder. + const assetsPath = path.join(rootPath, testFolderName, 'testAssets'); + if (!fs.existsSync(assetsPath)) { + throw new Error(`Could not find test assets at ${assetsPath}`); + } + const workspacePath = path.join(assetsPath, testAssetName, '.vscode', workspaceFileName); + if (!fs.existsSync(workspacePath)) { + throw new Error(`Could not find vscode workspace to open at ${workspacePath}`); + } + + // The runner (that loads in the vscode process to run tests) is in the test folder in the *output* directory. + const vscodeRunnerPath = path.join(outPath, testFolderName, 'index.js'); + if (!fs.existsSync(vscodeRunnerPath)) { + throw new Error(`Could not find vscode runner in out/ at ${vscodeRunnerPath}`); + } + + // Configure the file and suite name in CI to avoid having multiple test runs stomp on each other. + env.JEST_JUNIT_OUTPUT_NAME = getJUnitFileName(logName); + env.JEST_SUITE_NAME = suiteName; + + if (testFile) { + console.log(`Setting test file filter to: ${testFile}`); + process.env.TEST_FILE_FILTER = testFile; + } + + try { + const result = await prepareVSCodeAndExecuteTests(rootPath, vscodeRunnerPath, workspacePath, userDataDir, env); + if (result > 0) { + // The VSCode API will generally throw if jest fails the test, but we can get errors before the test runs (e.g. launching VSCode). + // So here we make sure to error if we don't get a clean exit code. + throw new Error(`Exit code: ${result}`); + } + + return result; + } catch (err) { + // If we hit an error, copy the logs VSCode produced to a directory that CI can find. + const vscodeLogs = path.join(userDataDir, 'logs'); + const logOutputPath = path.join(outPath, 'logs', logName); + console.log(`Copying logs from ${vscodeLogs} to ${logOutputPath}`); + fs.cpSync(vscodeLogs, logOutputPath, { recursive: true, force: true }); + + throw err; + } +} + +export function getJUnitFileName(logName: string) { + return `${logName.replaceAll(' ', '_')}_junit.xml`; +} diff --git a/tasks/testTasks.ts b/tasks/testTasks.ts index e9d506367..49d2d6b1e 100644 --- a/tasks/testTasks.ts +++ b/tasks/testTasks.ts @@ -3,17 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import fs from 'fs'; import * as gulp from 'gulp'; import * as path from 'path'; -import { codeExtensionPath, rootPath, outPath } from './projectPaths'; +import { codeExtensionPath, rootPath } from './projectPaths'; import * as jest from 'jest'; import { Config } from '@jest/types'; import { jestOmniSharpUnitTestProjectName } from '../test/omnisharp/omnisharpUnitTests/jest.config'; import { jestUnitTestProjectName } from '../test/lsptoolshost/unitTests/jest.config'; import { razorTestProjectName } from '../test/razor/razorTests/jest.config'; import { jestArtifactTestsProjectName } from '../test/lsptoolshost/artifactTests/jest.config'; -import { prepareVSCodeAndExecuteTests } from '../test/vscodeLauncher'; +import { + getJUnitFileName, + integrationTestProjects, + runDevKitIntegrationTests, + runIntegrationTest, + runJestIntegrationTest, +} from './testHelpers'; + +const razorIntegrationTestProjects = ['RazorApp']; createUnitTestSubTasks(); createIntegrationTestSubTasks(); @@ -50,7 +57,6 @@ function createUnitTestSubTasks() { } function createIntegrationTestSubTasks() { - const integrationTestProjects = ['slnWithCsproj']; for (const projectName of integrationTestProjects) { gulp.task(`test:integration:csharp:${projectName}`, async () => runIntegrationTest(projectName, path.join('lsptoolshost', 'integrationTests'), `[C#][${projectName}]`) @@ -75,7 +81,6 @@ function createIntegrationTestSubTasks() { gulp.series(integrationTestProjects.map((projectName) => `test:integration:devkit:${projectName}`)) ); - const razorIntegrationTestProjects = ['RazorApp']; for (const projectName of razorIntegrationTestProjects) { gulp.task(`test:razorintegration:${projectName}`, async () => runIntegrationTest( @@ -159,125 +164,6 @@ async function runOmnisharpJestIntegrationTest(testAssetName: string, engine: 's await runJestIntegrationTest(testAssetName, testFolder, workspaceFile, suiteName, env); } -async function runDevKitIntegrationTests(testAssetName: string, testFolderName: string, suiteName: string) { - // Tests using C# Dev Kit tests are a bit different from the rest - we are not able to restart the Dev Kit server and there - // are not easy APIs to use to know if the project is reloading due to workspace changes. - // So we have to isolate the C# Dev Kit tests into smaller test runs (in this case, per file), where each run - // launches VSCode and runs the tests in that file. - const testFolder = path.join(rootPath, 'test', testFolderName); - console.log(`Searching for test files in ${testFolder}`); - const allFiles = fs - .readdirSync(testFolder, { - recursive: true, - }) - .filter((f) => typeof f === 'string'); - const devKitTestFiles = allFiles.filter((f) => f.endsWith('.test.ts')).map((f) => path.join(testFolder, f)); - if (devKitTestFiles.length === 0) { - throw new Error(`No test files found in ${testFolder}`); - } - - let failed: boolean = false; - for (const testFile of devKitTestFiles) { - try { - await runIntegrationTest( - testAssetName, - testFolderName, - suiteName, - `devkit_${testAssetName}.code-workspace`, - testFile - ); - } catch (err) { - // We have to catch the error to continue running tests from the rest of the files. - console.error(`##[error] Tests in ${path.basename(testFile)} failed`, err); - failed = true; - } - } - - if (failed) { - // Ensure the task fails if any tests failed. - throw new Error(`One or more tests failed`); - } -} - -async function runIntegrationTest( - testAssetName: string, - testFolderName: string, - suiteName: string, - vscodeWorkspaceFileName = `${testAssetName}.code-workspace`, - testFile: string | undefined = undefined -) { - const testFolder = path.join('test', testFolderName); - const env: NodeJS.ProcessEnv = {}; - return await runJestIntegrationTest(testAssetName, testFolder, vscodeWorkspaceFileName, suiteName, env, testFile); -} - -/** - * Runs jest based integration tests. - * @param testAssetName the name of the test asset - * @param testFolderName the relative path (from workspace root) - * @param workspaceFileName the name of the vscode workspace file to use. - * @param suiteName a unique name for the test suite being run. - * @param env any environment variables needed. - * @param testFile the full path to a specific test file to run. - */ -async function runJestIntegrationTest( - testAssetName: string, - testFolderName: string, - workspaceFileName: string, - suiteName: string, - env: NodeJS.ProcessEnv = {}, - testFile: string | undefined = undefined -) { - const logName = testFile ? `${suiteName}_${path.basename(testFile)}` : suiteName; - - // Set VSCode to produce logs in a unique directory for this test run. - const userDataDir = path.join(outPath, 'userData', logName); - - // Test assets are always in a testAssets folder inside the integration test folder. - const assetsPath = path.join(rootPath, testFolderName, 'testAssets'); - if (!fs.existsSync(assetsPath)) { - throw new Error(`Could not find test assets at ${assetsPath}`); - } - const workspacePath = path.join(assetsPath, testAssetName, '.vscode', workspaceFileName); - if (!fs.existsSync(workspacePath)) { - throw new Error(`Could not find vscode workspace to open at ${workspacePath}`); - } - - // The runner (that loads in the vscode process to run tests) is in the test folder in the *output* directory. - const vscodeRunnerPath = path.join(outPath, testFolderName, 'index.js'); - if (!fs.existsSync(vscodeRunnerPath)) { - throw new Error(`Could not find vscode runner in out/ at ${vscodeRunnerPath}`); - } - - // Configure the file and suite name in CI to avoid having multiple test runs stomp on each other. - env.JEST_JUNIT_OUTPUT_NAME = getJUnitFileName(logName); - env.JEST_SUITE_NAME = suiteName; - - if (testFile) { - console.log(`Setting test file filter to: ${testFile}`); - process.env.TEST_FILE_FILTER = testFile; - } - - try { - const result = await prepareVSCodeAndExecuteTests(rootPath, vscodeRunnerPath, workspacePath, userDataDir, env); - if (result > 0) { - // The VSCode API will generally throw if jest fails the test, but we can get errors before the test runs (e.g. launching VSCode). - // So here we make sure to error if we don't get a clean exit code. - throw new Error(`Exit code: ${result}`); - } - - return result; - } catch (err) { - // If we hit an error, copy the logs VSCode produced to a directory that CI can find. - const vscodeLogs = path.join(userDataDir, 'logs'); - const logOutputPath = path.join(outPath, 'logs', logName); - console.log(`Copying logs from ${vscodeLogs} to ${logOutputPath}`); - fs.cpSync(vscodeLogs, logOutputPath, { recursive: true, force: true }); - - throw err; - } -} - async function runJestTest(project: string) { process.env.JEST_JUNIT_OUTPUT_NAME = getJUnitFileName(project); process.env.JEST_SUITE_NAME = project; @@ -295,7 +181,3 @@ async function runJestTest(project: string) { throw new Error('Tests failed.'); } } - -function getJUnitFileName(logName: string) { - return `${logName.replaceAll(' ', '_')}_junit.xml`; -} From 48fbd7836f7a45a3b5350cd0c5168e55e69af634 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Wed, 5 Feb 2025 10:10:22 +1100 Subject: [PATCH 11/20] Skip completion tests --- .../razorIntegrationTests/completion.integration.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/razor/razorIntegrationTests/completion.integration.test.ts b/test/razor/razorIntegrationTests/completion.integration.test.ts index 0760f1d60..53d056986 100644 --- a/test/razor/razorIntegrationTests/completion.integration.test.ts +++ b/test/razor/razorIntegrationTests/completion.integration.test.ts @@ -5,11 +5,11 @@ import * as path from 'path'; import * as vscode from 'vscode'; -import { beforeAll, afterAll, test, expect, beforeEach } from '@jest/globals'; +import { beforeAll, afterAll, test, expect, beforeEach, describe } from '@jest/globals'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; import * as integrationHelpers from '../../lsptoolshost/integrationTests/integrationHelpers'; -integrationHelpers.describeIfWindows(`Razor Completion ${testAssetWorkspace.description}`, function () { +describe.skip(`Razor Completion ${testAssetWorkspace.description}`, function () { beforeAll(async function () { if (!integrationHelpers.isRazorWorkspace(vscode.workspace)) { return; From 1b71e477630b304c2a5d507fd1d7cd7d7e5f1a83 Mon Sep 17 00:00:00 2001 From: GitOps <1esptcore@microsoft.com> Date: Wed, 5 Feb 2025 00:50:44 +0000 Subject: [PATCH 12/20] Updated for https://dev.azure.com/dnceng/7ea9116e-9fac-403d-b258-b31fcf1bb293/_build?definitionId=1424 by using baselines generated in https://dev.azure.com/dnceng/7ea9116e-9fac-403d-b258-b31fcf1bb293/_build/results?buildId=2635181 --- .config/1espt/PipelineAutobaseliningConfig.yml | 18 ++++++++++++++++++ .config/guardian/.gdnbaselines | 10 ++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.config/1espt/PipelineAutobaseliningConfig.yml b/.config/1espt/PipelineAutobaseliningConfig.yml index eec0d9373..3b670042d 100644 --- a/.config/1espt/PipelineAutobaseliningConfig.yml +++ b/.config/1espt/PipelineAutobaseliningConfig.yml @@ -50,3 +50,21 @@ pipelines: lastModifiedDate: 2024-03-27 armory: lastModifiedDate: 2024-03-27 + 1424: + retail: + source: + credscan: + lastModifiedDate: 2025-02-05 + eslint: + lastModifiedDate: 2025-02-05 + psscriptanalyzer: + lastModifiedDate: 2025-02-05 + armory: + lastModifiedDate: 2025-02-05 + binary: + credscan: + lastModifiedDate: 2025-02-05 + binskim: + lastModifiedDate: 2025-02-05 + spotbugs: + lastModifiedDate: 2025-02-05 diff --git a/.config/guardian/.gdnbaselines b/.config/guardian/.gdnbaselines index 631aaa846..9d323d101 100644 --- a/.config/guardian/.gdnbaselines +++ b/.config/guardian/.gdnbaselines @@ -14,10 +14,16 @@ "26445e3e484940d2d58c2ffc32ab3895fca4b1589d66e2f2dee2fa01f2c479fb": { "signature": "26445e3e484940d2d58c2ffc32ab3895fca4b1589d66e2f2dee2fa01f2c479fb", "alternativeSignatures": [], + "target": "test/omnisharp/omnisharpUnitTests/testAssets/private.pem", + "line": 1, "memberOf": [ "default" ], - "createdDate": "2024-09-09 19:35:36Z" + "tool": "credscan", + "ruleId": "CSCAN-GENERAL0020", + "createdDate": "2025-02-05 00:31:10Z", + "expirationDate": "2025-07-25 00:50:30Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-02-05 00:50:30Z" } } -} +} \ No newline at end of file From 69e52b97fa1a5f4f2888a040ae4e31ca8663dee2 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Wed, 5 Feb 2025 17:20:54 -0800 Subject: [PATCH 13/20] Sync whitespace options even when detectIndentation is on --- .../universalEditorConfigProvider.ts | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/lsptoolshost/universalEditorConfigProvider.ts b/src/lsptoolshost/universalEditorConfigProvider.ts index c65fc57e5..45ca26cf5 100644 --- a/src/lsptoolshost/universalEditorConfigProvider.ts +++ b/src/lsptoolshost/universalEditorConfigProvider.ts @@ -29,11 +29,11 @@ export function readEquivalentVsCodeConfiguration(serverSideOptionName: string): } function readTabSize(configuration: WorkspaceConfiguration): string { - return readValueIfDetectIndentationIsOff(configuration, 'editor.tabSize', '4'); + return readVsCodeConfigurations(configuration, 'editor.tabSize'); } function readIndentSize(configuration: WorkspaceConfiguration): string { - const indentSize = readValueIfDetectIndentationIsOff(configuration, 'editor.indentSize', '4'); + const indentSize = readVsCodeConfigurations(configuration, 'editor.indentSize'); // indent size could be a number or 'tabSize'. If it is 'tabSize', read the 'tabSize' section from config. if (indentSize == 'tabSize') { return readTabSize(configuration); @@ -43,7 +43,7 @@ function readIndentSize(configuration: WorkspaceConfiguration): string { } function readInsertSpaces(configuration: WorkspaceConfiguration): string { - const insertSpace = readValueIfDetectIndentationIsOff(configuration, 'editor.insertSpaces', true); + const insertSpace = readVsCodeConfigurations(configuration, 'editor.insertSpaces'); return insertSpace ? 'space' : 'tab'; } @@ -62,21 +62,6 @@ function readInsertFinalNewline(configuration: WorkspaceConfiguration): string { return readVsCodeConfigurations(configuration, 'files.insertFinalNewline'); } -function readValueIfDetectIndentationIsOff( - configuration: WorkspaceConfiguration, - vscodeConfigName: string, - defaultValue: T -): T { - // If detectIndentation is on, tabSize, indentSize and insertSpaces would be overridden by vscode based on the file's content. - // The values in settings become meaningless, so ask the server to fall back to default value. - // TODO: Both 'editor.detectIndentation' and.editorconfig provided the same functions here, we need to find a graceful way to handle them. - if (configuration.get('editor.detectIndentation')) { - return defaultValue; - } - - return readVsCodeConfigurations(configuration, vscodeConfigName); -} - function readVsCodeConfigurations(configuration: WorkspaceConfiguration, vscodeConfigName: string): T { const configValue = configuration.get(vscodeConfigName); if (configValue === undefined) { From 435f8ca2f792923d3a5d0005037d9b41ffdad379 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Thu, 6 Feb 2025 14:46:13 -0800 Subject: [PATCH 14/20] Apply feedback from code review --- snippets/csharp.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/snippets/csharp.json b/snippets/csharp.json index de5d30310..e37bc6cfb 100644 --- a/snippets/csharp.json +++ b/snippets/csharp.json @@ -357,8 +357,7 @@ "propex": { "prefix": "propex", "body": [ - "public ${1:int} ${2:MyProperty} => ${3:myVar};", - "$0" + "public ${1:int} ${2:MyProperty} => ${3:myVar};$0" ], "description": "An expression-bodied property without a backing field. C# 6.0 or higher" }, @@ -370,8 +369,7 @@ "{", "\tget => ${2:myVar};", "\tset => ${2:myVar} = value;", - "}", - "$0" + "}$0" ], "description": "An expression-bodied property with a private field. C# 6.0 or higher" }, From cb0aaf86744ecf29826e179703901a94e5310a93 Mon Sep 17 00:00:00 2001 From: Joey Robichaud Date: Thu, 6 Feb 2025 15:54:11 -0800 Subject: [PATCH 15/20] Organize LSP Host files into feature folders. --- l10n/bundle.l10n.json | 20 +- src/csharpExtensionExports.ts | 2 +- src/lsptoolshost/activate.ts | 175 ++++++++++ .../{ => autoInsert}/onAutoInsert.ts | 4 +- .../{ => autoInsert}/onAutoInsertFeature.ts | 2 +- src/lsptoolshost/commands.ts | 183 +---------- src/lsptoolshost/{ => copilot}/copilot.ts | 8 +- .../IDotnetDebugConfigurationService.ts | 0 src/lsptoolshost/{ => debugger}/debugger.ts | 19 +- ...slynWorkspaceDebugConfigurationProvider.ts | 12 +- .../buildDiagnosticsService.ts | 2 +- .../buildResultReporterService.ts | 2 +- .../{ => diagnostics}/fixAllCodeAction.ts | 6 +- .../{ => diagnostics}/nestedCodeAction.ts | 4 +- .../dotnetRuntimeExtensionApi.ts | 0 .../dotnetRuntimeExtensionResolver.ts | 10 +- .../{ => extensions}/builtInComponents.ts | 2 +- .../roslynLanguageServerExportChannel.ts | 2 +- .../sourceGeneratedFilesContentProvider.ts | 6 +- .../{ => handlers}/showToastNotification.ts | 6 +- src/lsptoolshost/languageStatusBar.ts | 99 ------ .../{ => options}/configurationMiddleware.ts | 0 .../{ => options}/optionChanges.ts | 8 +- .../{ => options}/optionNameConverter.ts | 0 .../universalEditorConfigProvider.ts | 0 src/lsptoolshost/{ => profiling}/profiling.ts | 0 .../projectContextService.ts | 9 +- .../projectContext/projectContextStatus.ts | 52 +++ .../{ => projectRestore}/restore.ts | 8 +- src/lsptoolshost/{ => razor}/razorCommands.ts | 6 +- .../{ => server}/languageServerEvents.ts | 15 +- .../{ => server}/roslynLanguageClient.ts | 6 +- .../{ => server}/roslynLanguageServer.ts | 298 +++++------------- .../{ => server}/roslynProtocol.ts | 2 +- src/lsptoolshost/server/serverCommands.ts | 156 +++++++++ .../brokeredServicesHosting.ts | 2 +- .../ISolutionSnapshotProvider.ts | 0 .../descriptors.ts | 0 .../solutionSnapshotProvider.ts | 2 +- src/lsptoolshost/{ => testing}/unitTesting.ts | 12 +- .../utils/combineDocumentSelectors.ts | 10 + .../isString.ts} | 12 +- src/lsptoolshost/{ => utils}/uriConverter.ts | 2 +- .../miscellaneousFileNotifier.ts | 8 +- .../workspace/workspaceCommands.ts | 39 +++ src/lsptoolshost/workspace/workspaceStatus.ts | 39 +++ src/main.ts | 18 +- .../src/codeActions/codeActionsHandler.ts | 4 +- src/razor/src/completion/completionHandler.ts | 4 +- .../src/diagnostics/razorDiagnosticHandler.ts | 4 +- .../src/document/razorDocumentManager.ts | 2 +- .../src/dynamicFile/dynamicFileInfoHandler.ts | 2 +- src/razor/src/extension.ts | 4 +- .../razorFormatNewFileHandler.ts | 2 +- src/razor/src/inlayHint/inlayHintHandler.ts | 4 +- .../src/inlayHint/inlayHintResolveHandler.ts | 2 +- .../simplify/razorSimplifyMethodHandler.ts | 4 +- src/shared/dotnetConfigurationProvider.ts | 6 +- tasks/projectPaths.ts | 2 +- .../buildDiagnostics.integration.test.ts | 5 +- .../documentDiagnostics.integration.test.ts | 2 +- .../integrationTests/integrationHelpers.ts | 2 +- .../unitTests.integration.test.ts | 2 +- .../workspaceDiagnostics.integration.test.ts | 2 +- .../unitTests/configurationMiddleware.test.ts | 2 +- ...languageServerConfigChangeObserver.test.ts | 2 +- 66 files changed, 710 insertions(+), 615 deletions(-) create mode 100644 src/lsptoolshost/activate.ts rename src/lsptoolshost/{ => autoInsert}/onAutoInsert.ts (97%) rename src/lsptoolshost/{ => autoInsert}/onAutoInsertFeature.ts (98%) rename src/lsptoolshost/{ => copilot}/copilot.ts (94%) rename src/lsptoolshost/{services => debugger}/IDotnetDebugConfigurationService.ts (100%) rename src/lsptoolshost/{ => debugger}/debugger.ts (73%) rename src/lsptoolshost/{ => debugger}/roslynWorkspaceDebugConfigurationProvider.ts (90%) rename src/lsptoolshost/{ => diagnostics}/buildDiagnosticsService.ts (99%) rename src/lsptoolshost/{services => diagnostics}/buildResultReporterService.ts (95%) rename src/lsptoolshost/{ => diagnostics}/fixAllCodeAction.ts (94%) rename src/lsptoolshost/{ => diagnostics}/nestedCodeAction.ts (97%) rename src/lsptoolshost/{ => dotnetRuntime}/dotnetRuntimeExtensionApi.ts (100%) rename src/lsptoolshost/{ => dotnetRuntime}/dotnetRuntimeExtensionResolver.ts (95%) rename src/lsptoolshost/{ => extensions}/builtInComponents.ts (97%) rename src/lsptoolshost/{ => extensions}/roslynLanguageServerExportChannel.ts (94%) rename src/lsptoolshost/{ => generators}/sourceGeneratedFilesContentProvider.ts (95%) rename src/lsptoolshost/{ => handlers}/showToastNotification.ts (88%) delete mode 100644 src/lsptoolshost/languageStatusBar.ts rename src/lsptoolshost/{ => options}/configurationMiddleware.ts (100%) rename src/lsptoolshost/{ => options}/optionChanges.ts (91%) rename src/lsptoolshost/{ => options}/optionNameConverter.ts (100%) rename src/lsptoolshost/{ => options}/universalEditorConfigProvider.ts (100%) rename src/lsptoolshost/{ => profiling}/profiling.ts (100%) rename src/lsptoolshost/{services => projectContext}/projectContextService.ts (94%) create mode 100644 src/lsptoolshost/projectContext/projectContextStatus.ts rename src/lsptoolshost/{ => projectRestore}/restore.ts (94%) rename src/lsptoolshost/{ => razor}/razorCommands.ts (96%) rename src/lsptoolshost/{ => server}/languageServerEvents.ts (82%) rename src/lsptoolshost/{ => server}/roslynLanguageClient.ts (93%) rename src/lsptoolshost/{ => server}/roslynLanguageServer.ts (80%) rename src/lsptoolshost/{ => server}/roslynProtocol.ts (99%) create mode 100644 src/lsptoolshost/server/serverCommands.ts rename src/lsptoolshost/{services => serviceBroker}/brokeredServicesHosting.ts (96%) rename src/lsptoolshost/{services => solutionSnapshot}/ISolutionSnapshotProvider.ts (100%) rename src/lsptoolshost/{services => solutionSnapshot}/descriptors.ts (100%) rename src/lsptoolshost/{services => solutionSnapshot}/solutionSnapshotProvider.ts (92%) rename src/lsptoolshost/{ => testing}/unitTesting.ts (95%) create mode 100644 src/lsptoolshost/utils/combineDocumentSelectors.ts rename src/lsptoolshost/{serverStateChange.ts => utils/isString.ts} (60%) rename src/lsptoolshost/{ => utils}/uriConverter.ts (92%) rename src/lsptoolshost/{ => workspace}/miscellaneousFileNotifier.ts (89%) create mode 100644 src/lsptoolshost/workspace/workspaceCommands.ts create mode 100644 src/lsptoolshost/workspace/workspaceStatus.ts diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 6ad19dccd..de04c7137 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -162,8 +162,14 @@ "Text editor must be focused to fix all issues": "Text editor must be focused to fix all issues", "Fix all issues": "Fix all issues", "Select fix all action": "Select fix all action", + "C# LSP Trace Logs": "C# LSP Trace Logs", + "Open solution": "Open solution", + "Restart server": "Restart server", + "C# Workspace Status": "C# Workspace Status", + "The active document is not part of the open workspace. Not all language features will be available.": "The active document is not part of the open workspace. Not all language features will be available.", + "Dismiss": "Dismiss", + "Do not show for this workspace": "Do not show for this workspace", "Test run already in progress": "Test run already in progress", - "Generated document not found": "Generated document not found", "Server stopped": "Server stopped", "Workspace projects": "Workspace projects", "Your workspace has multiple Visual Studio Solution files; please select one to get full IntelliSense.": "Your workspace has multiple Visual Studio Solution files; please select one to get full IntelliSense.", @@ -174,21 +180,15 @@ "IntelliCode features will not be available, {0} failed to activate.": "IntelliCode features will not be available, {0} failed to activate.", "Go to output": "Go to output", "Suppress notification": "Suppress notification", - "C# LSP Trace Logs": "C# LSP Trace Logs", "Restore {0}": "Restore {0}", "Restore already in progress": "Restore already in progress", "Sending request": "Sending request", + "C# Project Context Status": "C# Project Context Status", + "Active File Context": "Active File Context", "C# configuration has changed. Would you like to reload the window to apply your changes?": "C# configuration has changed. Would you like to reload the window to apply your changes?", + "Generated document not found": "Generated document not found", "Nested Code Action": "Nested Code Action", "Fix All: ": "Fix All: ", - "The active document is not part of the open workspace. Not all language features will be available.": "The active document is not part of the open workspace. Not all language features will be available.", - "Dismiss": "Dismiss", - "Do not show for this workspace": "Do not show for this workspace", - "Open solution": "Open solution", - "Restart server": "Restart server", - "C# Workspace Status": "C# Workspace Status", - "C# Project Context Status": "C# Project Context Status", - "Active File Context": "Active File Context", "Pick a fix all scope": "Pick a fix all scope", "Fix All Code Action": "Fix All Code Action", "Failed to set extension directory": "Failed to set extension directory", diff --git a/src/csharpExtensionExports.ts b/src/csharpExtensionExports.ts index 99bc7e372..13569b3fa 100644 --- a/src/csharpExtensionExports.ts +++ b/src/csharpExtensionExports.ts @@ -9,7 +9,7 @@ import { EventStream } from './eventStream'; import TestManager from './omnisharp/features/dotnetTest'; import { GlobalBrokeredServiceContainer } from '@microsoft/servicehub-framework'; import { RequestType } from 'vscode-languageclient/node'; -import { LanguageServerEvents } from './lsptoolshost/languageServerEvents'; +import { LanguageServerEvents } from './lsptoolshost/server/languageServerEvents'; export interface OmnisharpExtensionExports { initializationFinished: () => Promise; diff --git a/src/lsptoolshost/activate.ts b/src/lsptoolshost/activate.ts new file mode 100644 index 000000000..08534079c --- /dev/null +++ b/src/lsptoolshost/activate.ts @@ -0,0 +1,175 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as path from 'path'; +import { registerCommands } from './commands'; +import { registerDebugger } from './debugger/debugger'; +import { PlatformInformation } from '../shared/platform'; +import TelemetryReporter from '@vscode/extension-telemetry'; +import { getCSharpDevKit } from '../utils/getCSharpDevKit'; +import { DotnetRuntimeExtensionResolver } from './dotnetRuntime/dotnetRuntimeExtensionResolver'; +import { registerUnitTestingCommands } from './testing/unitTesting'; +import { registerLanguageServerOptionChanges } from './options/optionChanges'; +import { Observable } from 'rxjs'; +import { RoslynLanguageServerEvents } from './server/languageServerEvents'; +import { registerRazorCommands } from './razor/razorCommands'; +import { registerCodeActionFixAllCommands } from './diagnostics/fixAllCodeAction'; +import { commonOptions, languageServerOptions } from '../shared/options'; +import { registerNestedCodeActionCommands } from './diagnostics/nestedCodeAction'; +import { registerRestoreCommands } from './projectRestore/restore'; +import { registerCopilotExtension } from './copilot/copilot'; +import { registerSourceGeneratedFilesContentProvider } from './generators/sourceGeneratedFilesContentProvider'; +import { registerMiscellaneousFileNotifier } from './workspace/miscellaneousFileNotifier'; +import { TelemetryEventNames } from '../shared/telemetryEventNames'; +import { WorkspaceStatus } from './workspace/workspaceStatus'; +import { ProjectContextStatus } from './projectContext/projectContextStatus'; +import { RoslynLanguageServer } from './server/roslynLanguageServer'; + +let _channel: vscode.LogOutputChannel; +let _traceChannel: vscode.OutputChannel; + +/** + * Creates and activates the Roslyn language server. + * The returned promise will complete when the server starts. + */ +export async function activateRoslynLanguageServer( + context: vscode.ExtensionContext, + platformInfo: PlatformInformation, + optionObservable: Observable, + outputChannel: vscode.LogOutputChannel, + reporter: TelemetryReporter, + languageServerEvents: RoslynLanguageServerEvents +): Promise { + // Create a channel for outputting general logs from the language server. + _channel = outputChannel; + // Create a separate channel for outputting trace logs - these are incredibly verbose and make other logs very difficult to see. + // The trace channel verbosity is controlled by the _channel verbosity. + _traceChannel = vscode.window.createOutputChannel(vscode.l10n.t('C# LSP Trace Logs')); + + reporter.sendTelemetryEvent(TelemetryEventNames.ClientInitialize); + + const hostExecutableResolver = new DotnetRuntimeExtensionResolver( + platformInfo, + getServerPath, + outputChannel, + context.extensionPath + ); + const additionalExtensionPaths = scanExtensionPlugins(); + + const languageServer = await RoslynLanguageServer.initializeAsync( + platformInfo, + hostExecutableResolver, + context, + reporter, + additionalExtensionPaths, + languageServerEvents, + _channel, + _traceChannel + ); + + registerLanguageStatusItems(context, languageServer, languageServerEvents); + registerMiscellaneousFileNotifier(context, languageServer); + registerCopilotExtension(languageServer, _channel); + + // Register any commands that need to be handled by the extension. + registerCommands(context, languageServer, hostExecutableResolver, _channel); + registerNestedCodeActionCommands(context, languageServer, _channel); + registerCodeActionFixAllCommands(context, languageServer, _channel); + + registerRazorCommands(context, languageServer); + + registerUnitTestingCommands(context, languageServer); + + // Register any needed debugger components that need to communicate with the language server. + registerDebugger(context, languageServer, languageServerEvents, platformInfo, _channel); + + registerRestoreCommands(context, languageServer); + + registerSourceGeneratedFilesContentProvider(context, languageServer); + + context.subscriptions.push(registerLanguageServerOptionChanges(optionObservable)); + + return languageServer; + + function scanExtensionPlugins(): string[] { + const extensionsFromPackageJson = vscode.extensions.all.flatMap((extension) => { + let loadPaths = extension.packageJSON.contributes?.['csharpExtensionLoadPaths']; + if (loadPaths === undefined || loadPaths === null) { + _channel.debug(`Extension ${extension.id} does not contribute csharpExtensionLoadPaths`); + return []; + } + + if (!Array.isArray(loadPaths) || loadPaths.some((loadPath) => typeof loadPath !== 'string')) { + _channel.warn( + `Extension ${extension.id} has invalid csharpExtensionLoadPaths. Expected string array, found ${loadPaths}` + ); + return []; + } + + loadPaths = loadPaths.map((loadPath) => path.join(extension.extensionPath, loadPath)); + _channel.trace(`Extension ${extension.id} contributes csharpExtensionLoadPaths: ${loadPaths}`); + return loadPaths; + }); + const extensionsFromOptions = languageServerOptions.extensionsPaths ?? []; + return extensionsFromPackageJson.concat(extensionsFromOptions); + } +} + +function registerLanguageStatusItems( + context: vscode.ExtensionContext, + languageServer: RoslynLanguageServer, + languageServerEvents: RoslynLanguageServerEvents +) { + // DevKit will provide an equivalent workspace status item. + if (!getCSharpDevKit()) { + WorkspaceStatus.createStatusItem(context, languageServerEvents); + } + ProjectContextStatus.createStatusItem(context, languageServer); +} + +export function getServerPath(platformInfo: PlatformInformation) { + let serverPath = process.env.DOTNET_ROSLYN_SERVER_PATH; + + if (serverPath) { + _channel.appendLine(`Using server path override from DOTNET_ROSLYN_SERVER_PATH: ${serverPath}`); + } else { + serverPath = commonOptions.serverPath; + if (!serverPath) { + // Option not set, use the path from the extension. + serverPath = getInstalledServerPath(platformInfo); + } + } + + if (!fs.existsSync(serverPath)) { + throw new Error(`Cannot find language server in path '${serverPath}'`); + } + + return serverPath; +} + +function getInstalledServerPath(platformInfo: PlatformInformation): string { + const clientRoot = __dirname; + const serverFilePath = path.join(clientRoot, '..', '.roslyn', 'Microsoft.CodeAnalysis.LanguageServer'); + + let extension = ''; + if (platformInfo.isWindows()) { + extension = '.exe'; + } else if (platformInfo.isMacOS()) { + // MacOS executables must be signed with codesign. Currently all Roslyn server executables are built on windows + // and therefore dotnet publish does not automatically sign them. + // Tracking bug - https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1767519/ + extension = '.dll'; + } + + let pathWithExtension = `${serverFilePath}${extension}`; + if (!fs.existsSync(pathWithExtension)) { + // We might be running a platform neutral vsix which has no executable, instead we run the dll directly. + pathWithExtension = `${serverFilePath}.dll`; + } + + return pathWithExtension; +} diff --git a/src/lsptoolshost/onAutoInsert.ts b/src/lsptoolshost/autoInsert/onAutoInsert.ts similarity index 97% rename from src/lsptoolshost/onAutoInsert.ts rename to src/lsptoolshost/autoInsert/onAutoInsert.ts index 71ef4198a..3ecac2e1c 100644 --- a/src/lsptoolshost/onAutoInsert.ts +++ b/src/lsptoolshost/autoInsert/onAutoInsert.ts @@ -6,8 +6,8 @@ import * as vscode from 'vscode'; import { FormattingOptions, LanguageClient, TextDocumentIdentifier } from 'vscode-languageclient/node'; -import * as RoslynProtocol from './roslynProtocol'; -import { RoslynLanguageServer } from './roslynLanguageServer'; +import * as RoslynProtocol from '../server/roslynProtocol'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; export function registerOnAutoInsert(languageServer: RoslynLanguageServer, languageClient: LanguageClient) { let source = new vscode.CancellationTokenSource(); diff --git a/src/lsptoolshost/onAutoInsertFeature.ts b/src/lsptoolshost/autoInsert/onAutoInsertFeature.ts similarity index 98% rename from src/lsptoolshost/onAutoInsertFeature.ts rename to src/lsptoolshost/autoInsert/onAutoInsertFeature.ts index 88a1537b2..a8b48dbe7 100644 --- a/src/lsptoolshost/onAutoInsertFeature.ts +++ b/src/lsptoolshost/autoInsert/onAutoInsertFeature.ts @@ -21,7 +21,7 @@ import { ServerCapabilities, } from 'vscode-languageserver-protocol'; -import * as RoslynProtocol from './roslynProtocol'; +import * as RoslynProtocol from '../server/roslynProtocol'; import { generateUuid } from 'vscode-languageclient/lib/common/utils/uuid'; export class OnAutoInsertFeature implements DynamicFeature { diff --git a/src/lsptoolshost/commands.ts b/src/lsptoolshost/commands.ts index 5d8d1edf3..ad09e35fc 100644 --- a/src/lsptoolshost/commands.ts +++ b/src/lsptoolshost/commands.ts @@ -4,14 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { UriConverter } from './uriConverter'; -import * as languageClient from 'vscode-languageclient/node'; -import { RoslynLanguageServer } from './roslynLanguageServer'; -import { createLaunchTargetForSolution } from '../shared/launchTarget'; +import { RoslynLanguageServer } from './server/roslynLanguageServer'; import reportIssue from '../shared/reportIssue'; import { getDotnetInfo } from '../shared/utils/getDotnetInfo'; import { IHostExecutableResolver } from '../shared/constants/IHostExecutableResolver'; -import { getCSharpDevKit } from '../utils/getCSharpDevKit'; +import { registerWorkspaceCommands } from './workspace/workspaceCommands'; +import { registerServerCommands } from './server/serverCommands'; export function registerCommands( context: vscode.ExtensionContext, @@ -19,31 +17,20 @@ export function registerCommands( hostExecutableResolver: IHostExecutableResolver, outputChannel: vscode.LogOutputChannel ) { - // It is very important to be careful about the types used as parameters for these command callbacks. - // If the arguments are coming from the server as json, it is NOT appropriate to use type definitions - // from the normal vscode API (e.g. vscode.Location) as input parameters. - // - // This is because at runtime the json objects do not contain the expected prototypes that the vscode types - // have and will fail 'instanceof' checks that are sprinkled throught the vscode APIs. - // - // Instead, we define inputs from the server using the LSP type definitions as those have no prototypes - // so we don't accidentally pass them directly into vscode APIs. - context.subscriptions.push(vscode.commands.registerCommand('roslyn.client.peekReferences', peekReferencesCallback)); - context.subscriptions.push( - vscode.commands.registerCommand( - 'roslyn.client.completionComplexEdit', - async (textDocument, textEdit, isSnippetString, newOffset) => - completionComplexEdit(textDocument, textEdit, isSnippetString, newOffset, outputChannel) - ) - ); - context.subscriptions.push( - vscode.commands.registerCommand('dotnet.restartServer', async () => restartServer(languageServer)) - ); - if (!getCSharpDevKit()) { - context.subscriptions.push( - vscode.commands.registerCommand('dotnet.openSolution', async () => openSolution(languageServer)) - ); - } + registerExtensionCommands(context, languageServer, hostExecutableResolver, outputChannel); + registerWorkspaceCommands(context, languageServer); + registerServerCommands(context, languageServer, outputChannel); +} + +/** + * Register commands that drive the C# extension. + */ +function registerExtensionCommands( + context: vscode.ExtensionContext, + languageServer: RoslynLanguageServer, + hostExecutableResolver: IHostExecutableResolver, + outputChannel: vscode.LogOutputChannel +) { context.subscriptions.push( vscode.commands.registerCommand('csharp.reportIssue', async () => reportIssue( @@ -58,139 +45,3 @@ export function registerCommands( vscode.commands.registerCommand('csharp.showOutputWindow', async () => outputChannel.show()) ); } - -/** - * Callback for code lens commands. Executes a references request via the VSCode command - * which will call into the LSP server to get the data. Then calls the VSCode command to display the result. - * @param uriStr The uri containing the location to find references for. - * @param serverPosition The position json object to execute the find references request. - */ -async function peekReferencesCallback(uriStr: string, serverPosition: languageClient.Position): Promise { - const uri = UriConverter.deserialize(uriStr); - - // Convert the json position object into the corresponding vscode position type. - const vscodeApiPosition = new vscode.Position(serverPosition.line, serverPosition.character); - const references: vscode.Location[] = await vscode.commands.executeCommand( - 'vscode.executeReferenceProvider', - uri, - vscodeApiPosition - ); - - if (references && Array.isArray(references)) { - // The references could come back after the document has moved to a new state (that may not even contain the position). - // This is fine - the VSCode API is resilient to that scenario and will not crash. - await vscode.commands.executeCommand('editor.action.showReferences', uri, vscodeApiPosition, references); - } -} - -async function restartServer(languageServer: RoslynLanguageServer): Promise { - await languageServer.restart(); -} - -/** - * Callback after a completion item with complex edit is committed. The change needs to be made outside completion resolve - * handling - * - * IMPORTANT: @see RazorCompletionItemProvider.resolveCompletionItem matches the arguments for this commands - * so it can remap correctly in razor files. Any updates to this function signature requires updates there as well. - * - * @param uriStr The uri containing the location of the document where the completion item was committed in. - * @param textEdits The additional complex edit for the committed completion item. - * @param isSnippetString Indicates if the TextEdit contains a snippet string. - * @param newPosition The offset for new cursor position. -1 if the edit has not specified one. - */ -async function completionComplexEdit( - textDocument: languageClient.TextDocumentIdentifier, - textEdit: vscode.TextEdit, - isSnippetString: boolean, - newOffset: number, - outputChannel: vscode.LogOutputChannel -): Promise { - const componentName = '[roslyn.client.completionComplexEdit]'; - - // Find TextDocument, opening if needed. - const uri = UriConverter.deserialize(textDocument.uri); - const document = await vscode.workspace.openTextDocument(uri); - if (document === undefined) { - outputAndThrow(outputChannel, `${componentName} Can't open document with path: '${textDocument.uri}'`); - } - - // Use editor if we need to deal with selection or snippets. - let editor: vscode.TextEditor | undefined = undefined; - if (isSnippetString || newOffset >= 0) { - editor = await vscode.window.showTextDocument(document); - if (editor === undefined) { - outputAndThrow( - outputChannel, - `${componentName} Editor unavailable for document with path: '${textDocument.uri}'` - ); - } - } - - const newRange = document.validateRange( - new vscode.Range( - textEdit.range.start.line, - textEdit.range.start.character, - textEdit.range.end.line, - textEdit.range.end.character - ) - ); - - // HACK: - // ApplyEdit would fail the first time it's called when an item was committed with text modifying commit char (e.g. space, '(', etc.) - // so we retry a couple time here as a tempory workaround. We need to either figure our the reason of the failure, and/or try the - // approach of sending another edit request to server with updated document. - let success = false; - for (let i = 0; i < 3; i++) { - if (isSnippetString) { - editor!.selection = new vscode.Selection(newRange.start, newRange.end); - success = await editor!.insertSnippet(new vscode.SnippetString(textEdit.newText)); - } else { - const edit = new vscode.WorkspaceEdit(); - const newTextEdit = vscode.TextEdit.replace(newRange, textEdit.newText); - edit.set(document.uri, [newTextEdit]); - success = await vscode.workspace.applyEdit(edit); - - if (success && newOffset >= 0) { - const newPosition = document.positionAt(newOffset); - editor!.selections = [new vscode.Selection(newPosition, newPosition)]; - } - } - - if (success) { - return; - } - } - - if (!success) { - outputAndThrow( - outputChannel, - `${componentName} ${isSnippetString ? 'TextEditor.insertSnippet' : 'workspace.applyEdit'} failed.` - ); - } -} - -function outputAndThrow(outputChannel: vscode.LogOutputChannel, message: string): void { - outputChannel.show(); - outputChannel.error(message); - throw new Error(message); -} - -async function openSolution(languageServer: RoslynLanguageServer): Promise { - if (!vscode.workspace.workspaceFolders) { - return undefined; - } - - const solutionFiles = await vscode.workspace.findFiles('**/*.{sln,slnf}'); - const launchTargets = solutionFiles.map(createLaunchTargetForSolution); - const launchTarget = await vscode.window.showQuickPick(launchTargets, { - matchOnDescription: true, - placeHolder: `Select solution file`, - }); - - if (launchTarget) { - const uri = vscode.Uri.file(launchTarget.target); - await languageServer.openSolution(uri); - return uri; - } -} diff --git a/src/lsptoolshost/copilot.ts b/src/lsptoolshost/copilot/copilot.ts similarity index 94% rename from src/lsptoolshost/copilot.ts rename to src/lsptoolshost/copilot/copilot.ts index 491560609..651f671ec 100644 --- a/src/lsptoolshost/copilot.ts +++ b/src/lsptoolshost/copilot/copilot.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { CSharpExtensionId } from '../constants/csharpExtensionId'; -import { CopilotRelatedDocumentsReport, CopilotRelatedDocumentsRequest } from './roslynProtocol'; -import { RoslynLanguageServer } from './roslynLanguageServer'; -import { UriConverter } from './uriConverter'; +import { CSharpExtensionId } from '../../constants/csharpExtensionId'; +import { CopilotRelatedDocumentsReport, CopilotRelatedDocumentsRequest } from '../server/roslynProtocol'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; +import { UriConverter } from '../utils/uriConverter'; import { TextDocumentIdentifier } from 'vscode-languageserver-protocol'; interface CopilotTrait { diff --git a/src/lsptoolshost/services/IDotnetDebugConfigurationService.ts b/src/lsptoolshost/debugger/IDotnetDebugConfigurationService.ts similarity index 100% rename from src/lsptoolshost/services/IDotnetDebugConfigurationService.ts rename to src/lsptoolshost/debugger/IDotnetDebugConfigurationService.ts diff --git a/src/lsptoolshost/debugger.ts b/src/lsptoolshost/debugger/debugger.ts similarity index 73% rename from src/lsptoolshost/debugger.ts rename to src/lsptoolshost/debugger/debugger.ts index c7d2a3bc0..01c5b6d2c 100644 --- a/src/lsptoolshost/debugger.ts +++ b/src/lsptoolshost/debugger/debugger.ts @@ -4,16 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { addAssetsIfNecessary, generateAssets } from '../shared/assets'; -import { DotnetWorkspaceConfigurationProvider } from '../shared/workspaceConfigurationProvider'; -import { IWorkspaceDebugInformationProvider } from '../shared/IWorkspaceDebugInformationProvider'; -import { RoslynLanguageServer } from './roslynLanguageServer'; -import { RoslynWorkspaceDebugInformationProvider } from './roslynWorkspaceDebugConfigurationProvider'; -import { PlatformInformation } from '../shared/platform'; -import { ServerState } from './serverStateChange'; -import { DotnetConfigurationResolver } from '../shared/dotnetConfigurationProvider'; -import { getCSharpDevKit } from '../utils/getCSharpDevKit'; -import { RoslynLanguageServerEvents } from './languageServerEvents'; +import { addAssetsIfNecessary, generateAssets } from '../../shared/assets'; +import { DotnetWorkspaceConfigurationProvider } from '../../shared/workspaceConfigurationProvider'; +import { IWorkspaceDebugInformationProvider } from '../../shared/IWorkspaceDebugInformationProvider'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; +import { RoslynWorkspaceDebugInformationProvider } from '../debugger/roslynWorkspaceDebugConfigurationProvider'; +import { PlatformInformation } from '../../shared/platform'; +import { DotnetConfigurationResolver } from '../../shared/dotnetConfigurationProvider'; +import { getCSharpDevKit } from '../../utils/getCSharpDevKit'; +import { RoslynLanguageServerEvents, ServerState } from '../server/languageServerEvents'; export function registerDebugger( context: vscode.ExtensionContext, diff --git a/src/lsptoolshost/roslynWorkspaceDebugConfigurationProvider.ts b/src/lsptoolshost/debugger/roslynWorkspaceDebugConfigurationProvider.ts similarity index 90% rename from src/lsptoolshost/roslynWorkspaceDebugConfigurationProvider.ts rename to src/lsptoolshost/debugger/roslynWorkspaceDebugConfigurationProvider.ts index 2b5666c83..1c0b650c8 100644 --- a/src/lsptoolshost/roslynWorkspaceDebugConfigurationProvider.ts +++ b/src/lsptoolshost/debugger/roslynWorkspaceDebugConfigurationProvider.ts @@ -4,19 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { mapAsync } from '../common'; +import { mapAsync } from '../../common'; import { IWorkspaceDebugInformationProvider, ProjectDebugInformation, -} from '../shared/IWorkspaceDebugInformationProvider'; -import { isBlazorWebAssemblyHosted, isBlazorWebAssemblyProject, isWebProject } from '../shared/utils'; -import { RoslynLanguageServer } from './roslynLanguageServer'; +} from '../../shared/IWorkspaceDebugInformationProvider'; +import { isBlazorWebAssemblyHosted, isBlazorWebAssemblyProject, isWebProject } from '../../shared/utils'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; import { ProjectDebugConfiguration, WorkspaceDebugConfigurationParams, WorkspaceDebugConfigurationRequest, -} from './roslynProtocol'; -import { UriConverter } from './uriConverter'; +} from '../server/roslynProtocol'; +import { UriConverter } from '../utils/uriConverter'; export class RoslynWorkspaceDebugInformationProvider implements IWorkspaceDebugInformationProvider { constructor(private server: RoslynLanguageServer, private outputChannel: vscode.LogOutputChannel) {} diff --git a/src/lsptoolshost/buildDiagnosticsService.ts b/src/lsptoolshost/diagnostics/buildDiagnosticsService.ts similarity index 99% rename from src/lsptoolshost/buildDiagnosticsService.ts rename to src/lsptoolshost/diagnostics/buildDiagnosticsService.ts index 14ebea6cb..70458d3bd 100644 --- a/src/lsptoolshost/buildDiagnosticsService.ts +++ b/src/lsptoolshost/diagnostics/buildDiagnosticsService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { languageServerOptions } from '../shared/options'; +import { languageServerOptions } from '../../shared/options'; export enum AnalysisSetting { FullSolution = 'fullSolution', diff --git a/src/lsptoolshost/services/buildResultReporterService.ts b/src/lsptoolshost/diagnostics/buildResultReporterService.ts similarity index 95% rename from src/lsptoolshost/services/buildResultReporterService.ts rename to src/lsptoolshost/diagnostics/buildResultReporterService.ts index 4b26a8196..8055f36c5 100644 --- a/src/lsptoolshost/services/buildResultReporterService.ts +++ b/src/lsptoolshost/diagnostics/buildResultReporterService.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { RoslynLanguageServer } from '../roslynLanguageServer'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; import { CancellationToken } from 'vscode-jsonrpc'; import * as vscode from 'vscode'; diff --git a/src/lsptoolshost/fixAllCodeAction.ts b/src/lsptoolshost/diagnostics/fixAllCodeAction.ts similarity index 94% rename from src/lsptoolshost/fixAllCodeAction.ts rename to src/lsptoolshost/diagnostics/fixAllCodeAction.ts index 429b8ad9e..05fe9ea5a 100644 --- a/src/lsptoolshost/fixAllCodeAction.ts +++ b/src/lsptoolshost/diagnostics/fixAllCodeAction.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as RoslynProtocol from './roslynProtocol'; +import * as RoslynProtocol from '../server/roslynProtocol'; import { LSPAny } from 'vscode-languageserver-protocol'; -import { RoslynLanguageServer } from './roslynLanguageServer'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; import { URIConverter, createConverter } from 'vscode-languageclient/lib/common/protocolConverter'; -import { UriConverter } from './uriConverter'; +import { UriConverter } from '../utils/uriConverter'; export function registerCodeActionFixAllCommands( context: vscode.ExtensionContext, diff --git a/src/lsptoolshost/nestedCodeAction.ts b/src/lsptoolshost/diagnostics/nestedCodeAction.ts similarity index 97% rename from src/lsptoolshost/nestedCodeAction.ts rename to src/lsptoolshost/diagnostics/nestedCodeAction.ts index 633d6f19c..2cb148dc9 100644 --- a/src/lsptoolshost/nestedCodeAction.ts +++ b/src/lsptoolshost/diagnostics/nestedCodeAction.ts @@ -5,9 +5,9 @@ import * as vscode from 'vscode'; import { CodeAction, CodeActionResolveRequest, LSPAny } from 'vscode-languageserver-protocol'; -import { RoslynLanguageServer } from './roslynLanguageServer'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; import { URIConverter, createConverter } from 'vscode-languageclient/lib/common/protocolConverter'; -import { UriConverter } from './uriConverter'; +import { UriConverter } from '../utils/uriConverter'; import { getFixAllResponse } from './fixAllCodeAction'; export function registerNestedCodeActionCommands( diff --git a/src/lsptoolshost/dotnetRuntimeExtensionApi.ts b/src/lsptoolshost/dotnetRuntime/dotnetRuntimeExtensionApi.ts similarity index 100% rename from src/lsptoolshost/dotnetRuntimeExtensionApi.ts rename to src/lsptoolshost/dotnetRuntime/dotnetRuntimeExtensionApi.ts diff --git a/src/lsptoolshost/dotnetRuntimeExtensionResolver.ts b/src/lsptoolshost/dotnetRuntime/dotnetRuntimeExtensionResolver.ts similarity index 95% rename from src/lsptoolshost/dotnetRuntimeExtensionResolver.ts rename to src/lsptoolshost/dotnetRuntime/dotnetRuntimeExtensionResolver.ts index 174e72bc5..099fc23ab 100644 --- a/src/lsptoolshost/dotnetRuntimeExtensionResolver.ts +++ b/src/lsptoolshost/dotnetRuntime/dotnetRuntimeExtensionResolver.ts @@ -5,12 +5,12 @@ import * as path from 'path'; import * as vscode from 'vscode'; -import { HostExecutableInformation } from '../shared/constants/hostExecutableInformation'; -import { IHostExecutableResolver } from '../shared/constants/IHostExecutableResolver'; -import { PlatformInformation } from '../shared/platform'; -import { languageServerOptions } from '../shared/options'; +import { HostExecutableInformation } from '../../shared/constants/hostExecutableInformation'; +import { IHostExecutableResolver } from '../../shared/constants/IHostExecutableResolver'; +import { PlatformInformation } from '../../shared/platform'; +import { languageServerOptions } from '../../shared/options'; import { existsSync } from 'fs'; -import { CSharpExtensionId } from '../constants/csharpExtensionId'; +import { CSharpExtensionId } from '../../constants/csharpExtensionId'; import { readFile } from 'fs/promises'; import { IDotnetAcquireResult, IDotnetFindPathContext } from './dotnetRuntimeExtensionApi'; diff --git a/src/lsptoolshost/builtInComponents.ts b/src/lsptoolshost/extensions/builtInComponents.ts similarity index 97% rename from src/lsptoolshost/builtInComponents.ts rename to src/lsptoolshost/extensions/builtInComponents.ts index 4fa3007cc..e0a96218b 100644 --- a/src/lsptoolshost/builtInComponents.ts +++ b/src/lsptoolshost/extensions/builtInComponents.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import { LanguageServerOptions } from '../shared/options'; +import { LanguageServerOptions } from '../../shared/options'; interface ComponentInfo { defaultFolderName: string; diff --git a/src/lsptoolshost/roslynLanguageServerExportChannel.ts b/src/lsptoolshost/extensions/roslynLanguageServerExportChannel.ts similarity index 94% rename from src/lsptoolshost/roslynLanguageServerExportChannel.ts rename to src/lsptoolshost/extensions/roslynLanguageServerExportChannel.ts index be5372881..992a7d232 100644 --- a/src/lsptoolshost/roslynLanguageServerExportChannel.ts +++ b/src/lsptoolshost/extensions/roslynLanguageServerExportChannel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; import { RequestType } from 'vscode-languageclient/node'; -import { RoslynLanguageServer } from './roslynLanguageServer'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; export class RoslynLanguageServerExport { constructor(private _serverInitialized: Promise) {} diff --git a/src/lsptoolshost/sourceGeneratedFilesContentProvider.ts b/src/lsptoolshost/generators/sourceGeneratedFilesContentProvider.ts similarity index 95% rename from src/lsptoolshost/sourceGeneratedFilesContentProvider.ts rename to src/lsptoolshost/generators/sourceGeneratedFilesContentProvider.ts index 1e31e78c8..fadd2d86b 100644 --- a/src/lsptoolshost/sourceGeneratedFilesContentProvider.ts +++ b/src/lsptoolshost/generators/sourceGeneratedFilesContentProvider.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as RoslynProtocol from './roslynProtocol'; -import { RoslynLanguageServer } from './roslynLanguageServer'; -import { UriConverter } from './uriConverter'; +import * as RoslynProtocol from '../server/roslynProtocol'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; import * as lsp from 'vscode-languageserver-protocol'; import { IDisposable } from '@microsoft/servicehub-framework'; +import { UriConverter } from '../utils/uriConverter'; export function registerSourceGeneratedFilesContentProvider( context: vscode.ExtensionContext, diff --git a/src/lsptoolshost/showToastNotification.ts b/src/lsptoolshost/handlers/showToastNotification.ts similarity index 88% rename from src/lsptoolshost/showToastNotification.ts rename to src/lsptoolshost/handlers/showToastNotification.ts index f2e2c972c..3defcbe66 100644 --- a/src/lsptoolshost/showToastNotification.ts +++ b/src/lsptoolshost/handlers/showToastNotification.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { RoslynLanguageClient } from './roslynLanguageClient'; +import { RoslynLanguageClient } from '../server/roslynLanguageClient'; import { MessageType } from 'vscode-languageserver-protocol'; -import { ShowToastNotification } from './roslynProtocol'; -import { showErrorMessage, showInformationMessage, showWarningMessage } from '../shared/observers/utils/showMessage'; +import { ShowToastNotification } from '../server/roslynProtocol'; +import { showErrorMessage, showInformationMessage, showWarningMessage } from '../../shared/observers/utils/showMessage'; export function registerShowToastNotification(client: RoslynLanguageClient) { client.onNotification(ShowToastNotification.type, async (notification) => { diff --git a/src/lsptoolshost/languageStatusBar.ts b/src/lsptoolshost/languageStatusBar.ts deleted file mode 100644 index d0656e47f..000000000 --- a/src/lsptoolshost/languageStatusBar.ts +++ /dev/null @@ -1,99 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { RoslynLanguageServer } from './roslynLanguageServer'; -import { RoslynLanguageServerEvents } from './languageServerEvents'; -import { languageServerOptions } from '../shared/options'; -import { ServerState } from './serverStateChange'; -import { getCSharpDevKit } from '../utils/getCSharpDevKit'; -import { RazorLanguage } from '../razor/src/razorLanguage'; - -export function registerLanguageStatusItems( - context: vscode.ExtensionContext, - languageServer: RoslynLanguageServer, - languageServerEvents: RoslynLanguageServerEvents -) { - // DevKit will provide an equivalent workspace status item. - if (!getCSharpDevKit()) { - WorkspaceStatus.createStatusItem(context, languageServerEvents); - } - ProjectContextStatus.createStatusItem(context, languageServer); -} - -function combineDocumentSelectors(...selectors: vscode.DocumentSelector[]): vscode.DocumentSelector { - return selectors.reduce<(string | vscode.DocumentFilter)[]>((acc, selector) => acc.concat(selector), []); -} - -class WorkspaceStatus { - static createStatusItem(context: vscode.ExtensionContext, languageServerEvents: RoslynLanguageServerEvents) { - const documentSelector = combineDocumentSelectors(languageServerOptions.documentSelector); - const openSolutionCommand = { - command: 'dotnet.openSolution', - title: vscode.l10n.t('Open solution'), - }; - const restartServerCommand = { - command: 'dotnet.restartServer', - title: vscode.l10n.t('Restart server'), - }; - - const item = vscode.languages.createLanguageStatusItem('csharp.workspaceStatus', documentSelector); - item.name = vscode.l10n.t('C# Workspace Status'); - item.severity = vscode.LanguageStatusSeverity.Error; - item.command = openSolutionCommand; - context.subscriptions.push(item); - - languageServerEvents.onServerStateChange((e) => { - item.text = e.workspaceLabel; - item.busy = e.state === ServerState.ProjectInitializationStarted; - item.severity = - e.state === ServerState.Stopped - ? vscode.LanguageStatusSeverity.Error - : vscode.LanguageStatusSeverity.Information; - item.command = e.state === ServerState.Stopped ? restartServerCommand : openSolutionCommand; - }); - } -} - -class ProjectContextStatus { - static createStatusItem(context: vscode.ExtensionContext, languageServer: RoslynLanguageServer) { - const documentSelector = combineDocumentSelectors( - languageServerOptions.documentSelector, - RazorLanguage.documentSelector - ); - const projectContextService = languageServer._projectContextService; - - const item = vscode.languages.createLanguageStatusItem('csharp.projectContextStatus', documentSelector); - item.name = vscode.l10n.t('C# Project Context Status'); - item.detail = vscode.l10n.t('Active File Context'); - context.subscriptions.push(item); - - projectContextService.onActiveFileContextChanged((e) => { - item.text = e.context._vs_label; - - // Show a warning when the active file is part of the Miscellaneous File workspace and - // project initialization is complete. - if (languageServer.state === ServerState.ProjectInitializationComplete) { - item.severity = - e.context._vs_is_miscellaneous && e.isVerified - ? vscode.LanguageStatusSeverity.Warning - : vscode.LanguageStatusSeverity.Information; - } else { - item.severity = vscode.LanguageStatusSeverity.Information; - } - - item.detail = e.context._vs_is_miscellaneous - ? vscode.l10n.t( - 'The active document is not part of the open workspace. Not all language features will be available.' - ) - : vscode.l10n.t('Active File Context'); - }); - - // Trigger a refresh, but don't block creation on the refresh completing. - projectContextService.refresh().catch((e) => { - throw new Error(`Error refreshing project context status ${e}`); - }); - } -} diff --git a/src/lsptoolshost/configurationMiddleware.ts b/src/lsptoolshost/options/configurationMiddleware.ts similarity index 100% rename from src/lsptoolshost/configurationMiddleware.ts rename to src/lsptoolshost/options/configurationMiddleware.ts diff --git a/src/lsptoolshost/optionChanges.ts b/src/lsptoolshost/options/optionChanges.ts similarity index 91% rename from src/lsptoolshost/optionChanges.ts rename to src/lsptoolshost/options/optionChanges.ts index 4a66b2228..0bc5f878d 100644 --- a/src/lsptoolshost/optionChanges.ts +++ b/src/lsptoolshost/options/optionChanges.ts @@ -5,10 +5,10 @@ import * as vscode from 'vscode'; import { Observable } from 'rxjs'; -import { CommonOptionsThatTriggerReload, LanguageServerOptionsThatTriggerReload } from '../shared/options'; -import { HandleOptionChanges, OptionChangeObserver, OptionChanges } from '../shared/observers/optionChangeObserver'; -import Disposable from '../disposable'; -import { CommandOption, showInformationMessage } from '../shared/observers/utils/showMessage'; +import { CommonOptionsThatTriggerReload, LanguageServerOptionsThatTriggerReload } from '../../shared/options'; +import { HandleOptionChanges, OptionChangeObserver, OptionChanges } from '../../shared/observers/optionChangeObserver'; +import Disposable from '../../disposable'; +import { CommandOption, showInformationMessage } from '../../shared/observers/utils/showMessage'; export function registerLanguageServerOptionChanges(optionObservable: Observable): Disposable { const optionChangeObserver: OptionChangeObserver = { diff --git a/src/lsptoolshost/optionNameConverter.ts b/src/lsptoolshost/options/optionNameConverter.ts similarity index 100% rename from src/lsptoolshost/optionNameConverter.ts rename to src/lsptoolshost/options/optionNameConverter.ts diff --git a/src/lsptoolshost/universalEditorConfigProvider.ts b/src/lsptoolshost/options/universalEditorConfigProvider.ts similarity index 100% rename from src/lsptoolshost/universalEditorConfigProvider.ts rename to src/lsptoolshost/options/universalEditorConfigProvider.ts diff --git a/src/lsptoolshost/profiling.ts b/src/lsptoolshost/profiling/profiling.ts similarity index 100% rename from src/lsptoolshost/profiling.ts rename to src/lsptoolshost/profiling/profiling.ts diff --git a/src/lsptoolshost/services/projectContextService.ts b/src/lsptoolshost/projectContext/projectContextService.ts similarity index 94% rename from src/lsptoolshost/services/projectContextService.ts rename to src/lsptoolshost/projectContext/projectContextService.ts index 8dd9589b7..33e120f18 100644 --- a/src/lsptoolshost/services/projectContextService.ts +++ b/src/lsptoolshost/projectContext/projectContextService.ts @@ -4,12 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { RoslynLanguageServer } from '../roslynLanguageServer'; -import { VSGetProjectContextsRequest, VSProjectContext, VSProjectContextList } from '../roslynProtocol'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; +import { VSGetProjectContextsRequest, VSProjectContext, VSProjectContextList } from '../server/roslynProtocol'; import { TextDocumentIdentifier } from 'vscode-languageserver-protocol'; -import { UriConverter } from '../uriConverter'; -import { LanguageServerEvents } from '../languageServerEvents'; -import { ServerState } from '../serverStateChange'; +import { UriConverter } from '../utils/uriConverter'; +import { LanguageServerEvents, ServerState } from '../server/languageServerEvents'; export interface ProjectContextChangeEvent { languageId: string; diff --git a/src/lsptoolshost/projectContext/projectContextStatus.ts b/src/lsptoolshost/projectContext/projectContextStatus.ts new file mode 100644 index 000000000..7dcfd43b3 --- /dev/null +++ b/src/lsptoolshost/projectContext/projectContextStatus.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; +import { languageServerOptions } from '../../shared/options'; +import { RazorLanguage } from '../../razor/src/razorLanguage'; +import { ServerState } from '../server/languageServerEvents'; +import { combineDocumentSelectors } from '../utils/combineDocumentSelectors'; + +export class ProjectContextStatus { + static createStatusItem(context: vscode.ExtensionContext, languageServer: RoslynLanguageServer) { + const documentSelector = combineDocumentSelectors( + languageServerOptions.documentSelector, + RazorLanguage.documentSelector + ); + const projectContextService = languageServer._projectContextService; + + const item = vscode.languages.createLanguageStatusItem('csharp.projectContextStatus', documentSelector); + item.name = vscode.l10n.t('C# Project Context Status'); + item.detail = vscode.l10n.t('Active File Context'); + context.subscriptions.push(item); + + projectContextService.onActiveFileContextChanged((e) => { + item.text = e.context._vs_label; + + // Show a warning when the active file is part of the Miscellaneous File workspace and + // project initialization is complete. + if (languageServer.state === ServerState.ProjectInitializationComplete) { + item.severity = + e.context._vs_is_miscellaneous && e.isVerified + ? vscode.LanguageStatusSeverity.Warning + : vscode.LanguageStatusSeverity.Information; + } else { + item.severity = vscode.LanguageStatusSeverity.Information; + } + + item.detail = e.context._vs_is_miscellaneous + ? vscode.l10n.t( + 'The active document is not part of the open workspace. Not all language features will be available.' + ) + : vscode.l10n.t('Active File Context'); + }); + + // Trigger a refresh, but don't block creation on the refresh completing. + projectContextService.refresh().catch((e) => { + throw new Error(`Error refreshing project context status ${e}`); + }); + } +} diff --git a/src/lsptoolshost/restore.ts b/src/lsptoolshost/projectRestore/restore.ts similarity index 94% rename from src/lsptoolshost/restore.ts rename to src/lsptoolshost/projectRestore/restore.ts index 83f035707..3b9e6c6c2 100644 --- a/src/lsptoolshost/restore.ts +++ b/src/lsptoolshost/projectRestore/restore.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { RoslynLanguageServer } from './roslynLanguageServer'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; import { RestorableProjects, RestoreParams, RestorePartialResult, RestoreRequest, ProjectNeedsRestoreRequest, -} from './roslynProtocol'; +} from '../server/roslynProtocol'; import path from 'path'; -import { showErrorMessage } from '../shared/observers/utils/showMessage'; -import { getCSharpDevKit } from '../utils/getCSharpDevKit'; +import { showErrorMessage } from '../../shared/observers/utils/showMessage'; +import { getCSharpDevKit } from '../../utils/getCSharpDevKit'; let _restoreInProgress = false; diff --git a/src/lsptoolshost/razorCommands.ts b/src/lsptoolshost/razor/razorCommands.ts similarity index 96% rename from src/lsptoolshost/razorCommands.ts rename to src/lsptoolshost/razor/razorCommands.ts index 58416f606..df4f4013e 100644 --- a/src/lsptoolshost/razorCommands.ts +++ b/src/lsptoolshost/razor/razorCommands.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { RoslynLanguageServer } from './roslynLanguageServer'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; import * as vscode from 'vscode'; import { DidChangeTextDocumentNotification, @@ -28,9 +28,9 @@ import { InlayHintParams, InlayHintRequest, } from 'vscode-languageclient/node'; -import SerializableSimplifyMethodParams from '../razor/src/simplify/serializableSimplifyMethodParams'; +import SerializableSimplifyMethodParams from '../../razor/src/simplify/serializableSimplifyMethodParams'; import { TextEdit } from 'vscode-html-languageservice'; -import { SerializableFormatNewFileParams } from '../razor/src/formatNewFile/serializableFormatNewFileParams'; +import { SerializableFormatNewFileParams } from '../../razor/src/formatNewFile/serializableFormatNewFileParams'; // These are commands that are invoked by the Razor extension, and are used to send LSP requests to the Roslyn LSP server export const roslynDidChangeCommand = 'roslyn.changeRazorCSharp'; diff --git a/src/lsptoolshost/languageServerEvents.ts b/src/lsptoolshost/server/languageServerEvents.ts similarity index 82% rename from src/lsptoolshost/languageServerEvents.ts rename to src/lsptoolshost/server/languageServerEvents.ts index 1cd623921..3aa9756ef 100644 --- a/src/lsptoolshost/languageServerEvents.ts +++ b/src/lsptoolshost/server/languageServerEvents.ts @@ -4,8 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ServerStateChangeEvent } from './serverStateChange'; -import { IDisposable } from '../disposable'; +import { IDisposable } from '../../disposable'; + +export enum ServerState { + Stopped = 0, + Started = 1, + ProjectInitializationStarted = 2, + ProjectInitializationComplete = 3, +} + +export interface ServerStateChangeEvent { + state: ServerState; + workspaceLabel: string; +} /** * Defines events that are fired by the language server. diff --git a/src/lsptoolshost/roslynLanguageClient.ts b/src/lsptoolshost/server/roslynLanguageClient.ts similarity index 93% rename from src/lsptoolshost/roslynLanguageClient.ts rename to src/lsptoolshost/server/roslynLanguageClient.ts index 5dad3ca0a..47266aedf 100644 --- a/src/lsptoolshost/roslynLanguageClient.ts +++ b/src/lsptoolshost/server/roslynLanguageClient.ts @@ -10,9 +10,9 @@ import { MessageSignature, ServerOptions, } from 'vscode-languageclient/node'; -import CompositeDisposable from '../compositeDisposable'; -import { IDisposable } from '../disposable'; -import { languageServerOptions } from '../shared/options'; +import CompositeDisposable from '../../compositeDisposable'; +import { IDisposable } from '../../disposable'; +import { languageServerOptions } from '../../shared/options'; /** * Implementation of the base LanguageClient type that allows for additional items to be disposed of diff --git a/src/lsptoolshost/roslynLanguageServer.ts b/src/lsptoolshost/server/roslynLanguageServer.ts similarity index 80% rename from src/lsptoolshost/roslynLanguageServer.ts rename to src/lsptoolshost/server/roslynLanguageServer.ts index 5345cae6b..b29c9d73c 100644 --- a/src/lsptoolshost/roslynLanguageServer.ts +++ b/src/lsptoolshost/server/roslynLanguageServer.ts @@ -4,15 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as fs from 'fs'; import * as path from 'path'; import * as cp from 'child_process'; import * as uuid from 'uuid'; import * as net from 'net'; -import { registerCommands } from './commands'; -import { registerDebugger } from './debugger'; -import { UriConverter } from './uriConverter'; - import { LanguageClientOptions, ServerOptions, @@ -31,58 +26,49 @@ import { ResponseError, NotificationHandler0, } from 'vscode-languageclient/node'; -import { PlatformInformation } from '../shared/platform'; -import { readConfigurations } from './configurationMiddleware'; -import { DynamicFileInfoHandler } from '../razor/src/dynamicFile/dynamicFileInfoHandler'; +import { PlatformInformation } from '../../shared/platform'; +import { readConfigurations } from '../options/configurationMiddleware'; +import { DynamicFileInfoHandler } from '../../razor/src/dynamicFile/dynamicFileInfoHandler'; import * as RoslynProtocol from './roslynProtocol'; -import { CSharpDevKitExports } from '../csharpDevKitExports'; -import { SolutionSnapshotId } from './services/ISolutionSnapshotProvider'; -import { ServerState } from './serverStateChange'; +import { CSharpDevKitExports } from '../../csharpDevKitExports'; +import { SolutionSnapshotId } from '../solutionSnapshot/ISolutionSnapshotProvider'; import TelemetryReporter from '@vscode/extension-telemetry'; -import CSharpIntelliCodeExports from '../csharpIntelliCodeExports'; -import { csharpDevkitExtensionId, csharpDevkitIntelliCodeExtensionId, getCSharpDevKit } from '../utils/getCSharpDevKit'; +import CSharpIntelliCodeExports from '../../csharpIntelliCodeExports'; +import { + csharpDevkitExtensionId, + csharpDevkitIntelliCodeExtensionId, + getCSharpDevKit, +} from '../../utils/getCSharpDevKit'; import { randomUUID } from 'crypto'; -import { DotnetRuntimeExtensionResolver } from './dotnetRuntimeExtensionResolver'; -import { IHostExecutableResolver } from '../shared/constants/IHostExecutableResolver'; +import { IHostExecutableResolver } from '../../shared/constants/IHostExecutableResolver'; import { RoslynLanguageClient } from './roslynLanguageClient'; -import { registerUnitTestingCommands } from './unitTesting'; -import { reportProjectConfigurationEvent } from '../shared/projectConfiguration'; -import { getDotnetInfo } from '../shared/utils/getDotnetInfo'; -import { registerLanguageServerOptionChanges } from './optionChanges'; -import { Observable } from 'rxjs'; -import { DotnetInfo } from '../shared/utils/dotnetInfo'; -import { RoslynLanguageServerEvents } from './languageServerEvents'; -import { registerShowToastNotification } from './showToastNotification'; -import { registerRazorCommands } from './razorCommands'; -import { registerOnAutoInsert } from './onAutoInsert'; -import { registerCodeActionFixAllCommands } from './fixAllCodeAction'; -import { commonOptions, languageServerOptions, omnisharpOptions, razorOptions } from '../shared/options'; +import { reportProjectConfigurationEvent } from '../../shared/projectConfiguration'; +import { getDotnetInfo } from '../../shared/utils/getDotnetInfo'; +import { DotnetInfo } from '../../shared/utils/dotnetInfo'; +import { RoslynLanguageServerEvents, ServerState } from './languageServerEvents'; +import { registerShowToastNotification } from '../handlers/showToastNotification'; +import { registerOnAutoInsert } from '../autoInsert/onAutoInsert'; +import { commonOptions, languageServerOptions, omnisharpOptions, razorOptions } from '../../shared/options'; import { NamedPipeInformation } from './roslynProtocol'; -import { IDisposable } from '../disposable'; -import { registerNestedCodeActionCommands } from './nestedCodeAction'; -import { registerRestoreCommands } from './restore'; -import { BuildDiagnosticsService } from './buildDiagnosticsService'; -import { getComponentPaths } from './builtInComponents'; -import { OnAutoInsertFeature } from './onAutoInsertFeature'; -import { registerLanguageStatusItems } from './languageStatusBar'; -import { ProjectContextService } from './services/projectContextService'; -import { ProvideDynamicFileResponse } from '../razor/src/dynamicFile/provideDynamicFileResponse'; -import { ProvideDynamicFileParams } from '../razor/src/dynamicFile/provideDynamicFileParams'; -import { registerCopilotExtension } from './copilot'; +import { IDisposable } from '../../disposable'; +import { BuildDiagnosticsService } from '../diagnostics/buildDiagnosticsService'; +import { getComponentPaths } from '../extensions/builtInComponents'; +import { OnAutoInsertFeature } from '../autoInsert/onAutoInsertFeature'; +import { ProjectContextService } from '../projectContext/projectContextService'; +import { ProvideDynamicFileResponse } from '../../razor/src/dynamicFile/provideDynamicFileResponse'; +import { ProvideDynamicFileParams } from '../../razor/src/dynamicFile/provideDynamicFileParams'; import { ActionOption, CommandOption, showErrorMessage, showInformationMessage, -} from '../shared/observers/utils/showMessage'; -import { registerSourceGeneratedFilesContentProvider } from './sourceGeneratedFilesContentProvider'; -import { registerMiscellaneousFileNotifier } from './miscellaneousFileNotifier'; -import { TelemetryEventNames } from '../shared/telemetryEventNames'; -import { RazorDynamicFileChangedParams } from '../razor/src/dynamicFile/dynamicFileUpdatedParams'; -import { getProfilingEnvVars } from './profiling'; - -let _channel: vscode.LogOutputChannel; -let _traceChannel: vscode.OutputChannel; +} from '../../shared/observers/utils/showMessage'; +import { TelemetryEventNames } from '../../shared/telemetryEventNames'; +import { RazorDynamicFileChangedParams } from '../../razor/src/dynamicFile/dynamicFileUpdatedParams'; +import { getProfilingEnvVars } from '../profiling/profiling'; +import { isString } from '../utils/isString'; +import { getServerPath } from '../activate'; +import { UriConverter } from '../utils/uriConverter'; // Flag indicating if C# Devkit was installed the last time we activated. // Used to determine if we need to restart the server on extension changes. @@ -130,7 +116,8 @@ export class RoslynLanguageServer { private _platformInfo: PlatformInformation, private _context: vscode.ExtensionContext, private _telemetryReporter: TelemetryReporter, - private _languageServerEvents: RoslynLanguageServerEvents + private _languageServerEvents: RoslynLanguageServerEvents, + private _channel: vscode.LogOutputChannel ) { this.registerSetTrace(); this.registerSendOpenSolution(); @@ -172,17 +159,17 @@ export class RoslynLanguageServer { } }); // Register for changes to the log level. - _channel.onDidChangeLogLevel(async () => { + this._channel.onDidChangeLogLevel(async () => { await this.updateLogLevel(); }); } private async updateLogLevel(): Promise { if (this._languageClient.state === State.Running) { - const languageClientTraceLevel = RoslynLanguageServer.GetTraceLevel(_channel.logLevel); + const languageClientTraceLevel = RoslynLanguageServer.GetTraceLevel(this._channel.logLevel); // Update the server's log level. await this.sendNotification('roslyn/updateLogLevel', { - logLevel: RoslynLanguageServer.GetServerLogLevel(_channel.logLevel), + logLevel: RoslynLanguageServer.GetServerLogLevel(this._channel.logLevel), }); // Update the trace level that the client uses to log trace messages. await this._languageClient.setTrace(languageClientTraceLevel); @@ -261,7 +248,9 @@ export class RoslynLanguageServer { context: vscode.ExtensionContext, telemetryReporter: TelemetryReporter, additionalExtensionPaths: string[], - languageServerEvents: RoslynLanguageServerEvents + languageServerEvents: RoslynLanguageServerEvents, + channel: vscode.LogOutputChannel, + traceChannel: vscode.OutputChannel ): Promise { const serverOptions: ServerOptions = async () => { return await this.startServer( @@ -269,7 +258,8 @@ export class RoslynLanguageServer { hostExecutableResolver, context, telemetryReporter, - additionalExtensionPaths + additionalExtensionPaths, + channel ); }; @@ -282,8 +272,8 @@ export class RoslynLanguageServer { synchronize: { fileEvents: [], }, - traceOutputChannel: _traceChannel, - outputChannel: _channel, + traceOutputChannel: traceChannel, + outputChannel: channel, uriConverters: { // VSCode encodes the ":" as "%3A" in file paths, for example "file:///c%3A/Users/dabarbet/source/repos/ConsoleApp8/ConsoleApp8/Program.cs". // System.Uri does not decode the LocalPath property correctly into a valid windows path, instead you get something like @@ -310,7 +300,14 @@ export class RoslynLanguageServer { client.registerProposedFeatures(); - const server = new RoslynLanguageServer(client, platformInfo, context, telemetryReporter, languageServerEvents); + const server = new RoslynLanguageServer( + client, + platformInfo, + context, + telemetryReporter, + languageServerEvents, + channel + ); client.registerFeature(server._onAutoInsertFeature); @@ -489,7 +486,7 @@ export class RoslynLanguageServer { } if (!(error instanceof vscode.CancellationError)) { - _channel.error(`Error making ${request} request`, error); + this._channel.error(`Error making ${request} request`, error); } return error; } @@ -580,14 +577,15 @@ export class RoslynLanguageServer { hostExecutableResolver: IHostExecutableResolver, context: vscode.ExtensionContext, telemetryReporter: TelemetryReporter, - additionalExtensionPaths: string[] + additionalExtensionPaths: string[], + channel: vscode.LogOutputChannel ): Promise { telemetryReporter.sendTelemetryEvent(TelemetryEventNames.ClientServerStart); const serverPath = getServerPath(platformInfo); const dotnetInfo = await hostExecutableResolver.getHostExecutableInfo(); const dotnetExecutablePath = dotnetInfo.path; - _channel.info('Dotnet path: ' + dotnetExecutablePath); + channel.info('Dotnet path: ' + dotnetExecutablePath); telemetryReporter.sendTelemetryEvent(TelemetryEventNames.AcquiredRuntime); let args: string[] = []; @@ -599,7 +597,7 @@ export class RoslynLanguageServer { // Get the initial log level from the channel. // Changes to the channel log level will be picked up by the server after // LSP finishes initializing and we're able to pick up the new value. - const logLevel = this.GetServerLogLevel(_channel.logLevel); + const logLevel = this.GetServerLogLevel(channel.logLevel); if (logLevel) { args.push('--logLevel', logLevel); } @@ -628,14 +626,15 @@ export class RoslynLanguageServer { csharpDevkitIntelliCodeExtensionId ); if (csharpDevkitIntelliCodeExtension) { - _channel.info('Activating C# + C# Dev Kit + C# IntelliCode...'); + channel.info('Activating C# + C# Dev Kit + C# IntelliCode...'); const csharpDevkitIntelliCodeArgs = await this.getCSharpDevkitIntelliCodeExportArgs( csharpDevkitIntelliCodeExtension, - context + context, + channel ); args = args.concat(csharpDevkitIntelliCodeArgs); } else { - _channel.info('Activating C# + C# Dev Kit...'); + channel.info('Activating C# + C# Dev Kit...'); } // Set command enablement as soon as we know devkit is available. @@ -647,7 +646,7 @@ export class RoslynLanguageServer { await this.setupDevKitEnvironment(dotnetInfo.env, csharpDevkitExtension); } else { // C# Dev Kit is not installed - continue C#-only activation. - _channel.info('Activating C# standalone...'); + channel.info('Activating C# standalone...'); // Set command enablement to use roslyn standalone commands. await vscode.commands.executeCommand('setContext', 'dotnet.server.activationContext', 'Roslyn'); @@ -658,7 +657,7 @@ export class RoslynLanguageServer { args.push('--extension', extensionPath); } - _channel.debug(`Starting server at ${serverPath}`); + channel.debug(`Starting server at ${serverPath}`); // shouldn't this arg only be set if it's running with CSDevKit? args.push('--telemetryLevel', telemetryReporter.telemetryLevel); @@ -669,13 +668,13 @@ export class RoslynLanguageServer { if (!languageServerOptions.useServerGC) { // The server by default uses serverGC, if the user opts out we need to set the environment variable to disable it. env.DOTNET_gcServer = '0'; - _channel.debug('ServerGC disabled'); + channel.debug('ServerGC disabled'); } - const profilingEnvVars = getProfilingEnvVars(_channel); + const profilingEnvVars = getProfilingEnvVars(channel); env = { ...env, ...profilingEnvVars }; - _channel.trace(`Profiling environment variables: ${JSON.stringify(profilingEnvVars)}`); + channel.trace(`Profiling environment variables: ${JSON.stringify(profilingEnvVars)}`); let childProcess: cp.ChildProcessWithoutNullStreams; const cpOptions: cp.SpawnOptionsWithoutStdio = { @@ -688,12 +687,12 @@ export class RoslynLanguageServer { // If we were given a path to a dll, launch that via dotnet. const argsWithPath = [serverPath].concat(args); - _channel.debug(`Server arguments ${argsWithPath.join(' ')}`); + channel.debug(`Server arguments ${argsWithPath.join(' ')}`); childProcess = cp.spawn(dotnetExecutablePath, argsWithPath, cpOptions); } else { // Otherwise assume we were given a path to an executable. - _channel.debug(`Server arguments ${args.join(' ')}`); + channel.debug(`Server arguments ${args.join(' ')}`); childProcess = cp.spawn(serverPath, args, cpOptions); } @@ -703,14 +702,14 @@ export class RoslynLanguageServer { // Record the stdout and stderr streams from the server process. childProcess.stdout.on('data', (data: { toString: (arg0: any) => any }) => { const result: string = isString(data) ? data : data.toString(RoslynLanguageServer.encoding); - _channel.info('[stdout] ' + result); + channel.info('[stdout] ' + result); }); childProcess.stderr.on('data', (data: { toString: (arg0: any) => any }) => { const result: string = isString(data) ? data : data.toString(RoslynLanguageServer.encoding); - _channel.error('[stderr] ' + result); + channel.error('[stderr] ' + result); }); childProcess.on('exit', (code) => { - _channel.info(`Language server process exited with ${code}`); + channel.info(`Language server process exited with ${code}`); }); // Timeout promise used to time out the connection process if it takes too long. @@ -730,14 +729,14 @@ export class RoslynLanguageServer { // The server process will create the named pipe used for communication. Wait for it to be created, // and listen for the server to pass back the connection information via stdout. const namedPipePromise = new Promise((resolve) => { - _channel.debug('waiting for named pipe information from server...'); + channel.debug('waiting for named pipe information from server...'); childProcess.stdout.on('data', (data: { toString: (arg0: any) => any }) => { const result: string = isString(data) ? data : data.toString(RoslynLanguageServer.encoding); // Use the regular expression to find all JSON lines const jsonLines = result.match(RoslynLanguageServer.namedPipeKeyRegex); if (jsonLines) { const transmittedPipeNameInfo: NamedPipeInformation = JSON.parse(jsonLines[0]); - _channel.info('received named pipe information from server'); + channel.info('received named pipe information from server'); resolve(transmittedPipeNameInfo); } }); @@ -745,9 +744,9 @@ export class RoslynLanguageServer { const socketPromise = namedPipePromise.then(async (pipeConnectionInfo) => { return new Promise((resolve, reject) => { - _channel.debug('attempting to connect client to server...'); + channel.debug('attempting to connect client to server...'); const socket = net.createConnection(pipeConnectionInfo.pipeName, () => { - _channel.info('client has connected to server'); + channel.info('client has connected to server'); resolve(socket); }); @@ -805,7 +804,7 @@ export class RoslynLanguageServer { notification ); } else { - _channel.warn('Tried to send razor/dynamicFileInfoChanged while server is not running'); + this._channel.warn('Tried to send razor/dynamicFileInfoChanged while server is not running'); } } ); @@ -917,7 +916,7 @@ export class RoslynLanguageServer { if (csharpDevkitExtension && !_wasActivatedWithCSharpDevkit) { // We previously started without C# Dev Kit and its now installed. // Offer a prompt to restart the server to use C# Dev Kit. - _channel.info(`Detected new installation of ${csharpDevkitExtensionId}`); + this._channel.info(`Detected new installation of ${csharpDevkitExtensionId}`); const message = `Detected installation of ${csharpDevkitExtensionId}. Would you like to relaunch the language server for added features?`; showInformationMessage(vscode, message, title); } else { @@ -967,7 +966,8 @@ export class RoslynLanguageServer { private static async getCSharpDevkitIntelliCodeExportArgs( csharpDevkitIntelliCodeExtension: vscode.Extension, - extensionContext: vscode.ExtensionContext + extensionContext: vscode.ExtensionContext, + channel: vscode.LogOutputChannel ): Promise { try { const exports = await csharpDevkitIntelliCodeExtension.activate(); @@ -980,9 +980,9 @@ export class RoslynLanguageServer { ]; return csharpIntelliCodeArgs; } catch (e) { - _channel.error(`Activation of ${csharpDevkitIntelliCodeExtensionId} failed`, e); + channel.error(`Activation of ${csharpDevkitIntelliCodeExtensionId} failed`, e); if (e instanceof Error && e.stack) { - _channel.info(e.stack); + channel.info(e.stack); } const stateKey = 'disableIntellicodeFailedPopup'; @@ -997,7 +997,7 @@ export class RoslynLanguageServer { const showOutput: ActionOption = { title: vscode.l10n.t('Go to output'), action: async () => { - _channel.show(); + channel.show(); }, }; const suppressNotification: ActionOption = { @@ -1085,134 +1085,6 @@ export class RoslynLanguageServer { } } -/** - * Creates and activates the Roslyn language server. - * The returned promise will complete when the server starts. - */ -export async function activateRoslynLanguageServer( - context: vscode.ExtensionContext, - platformInfo: PlatformInformation, - optionObservable: Observable, - outputChannel: vscode.LogOutputChannel, - reporter: TelemetryReporter, - languageServerEvents: RoslynLanguageServerEvents -): Promise { - // Create a channel for outputting general logs from the language server. - _channel = outputChannel; - // Create a separate channel for outputting trace logs - these are incredibly verbose and make other logs very difficult to see. - // The trace channel verbosity is controlled by the _channel verbosity. - _traceChannel = vscode.window.createOutputChannel(vscode.l10n.t('C# LSP Trace Logs')); - - reporter.sendTelemetryEvent(TelemetryEventNames.ClientInitialize); - - const hostExecutableResolver = new DotnetRuntimeExtensionResolver( - platformInfo, - getServerPath, - outputChannel, - context.extensionPath - ); - const additionalExtensionPaths = scanExtensionPlugins(); - - const languageServer = await RoslynLanguageServer.initializeAsync( - platformInfo, - hostExecutableResolver, - context, - reporter, - additionalExtensionPaths, - languageServerEvents - ); - - registerLanguageStatusItems(context, languageServer, languageServerEvents); - registerMiscellaneousFileNotifier(context, languageServer); - registerCopilotExtension(languageServer, _channel); - - // Register any commands that need to be handled by the extension. - registerCommands(context, languageServer, hostExecutableResolver, _channel); - registerNestedCodeActionCommands(context, languageServer, _channel); - registerCodeActionFixAllCommands(context, languageServer, _channel); - - registerRazorCommands(context, languageServer); - - registerUnitTestingCommands(context, languageServer); - - // Register any needed debugger components that need to communicate with the language server. - registerDebugger(context, languageServer, languageServerEvents, platformInfo, _channel); - - registerRestoreCommands(context, languageServer); - - registerSourceGeneratedFilesContentProvider(context, languageServer); - - context.subscriptions.push(registerLanguageServerOptionChanges(optionObservable)); - - return languageServer; - - function scanExtensionPlugins(): string[] { - const extensionsFromPackageJson = vscode.extensions.all.flatMap((extension) => { - let loadPaths = extension.packageJSON.contributes?.['csharpExtensionLoadPaths']; - if (loadPaths === undefined || loadPaths === null) { - _channel.debug(`Extension ${extension.id} does not contribute csharpExtensionLoadPaths`); - return []; - } - - if (!Array.isArray(loadPaths) || loadPaths.some((loadPath) => typeof loadPath !== 'string')) { - _channel.warn( - `Extension ${extension.id} has invalid csharpExtensionLoadPaths. Expected string array, found ${loadPaths}` - ); - return []; - } - - loadPaths = loadPaths.map((loadPath) => path.join(extension.extensionPath, loadPath)); - _channel.trace(`Extension ${extension.id} contributes csharpExtensionLoadPaths: ${loadPaths}`); - return loadPaths; - }); - const extensionsFromOptions = languageServerOptions.extensionsPaths ?? []; - return extensionsFromPackageJson.concat(extensionsFromOptions); - } -} - -function getServerPath(platformInfo: PlatformInformation) { - let serverPath = process.env.DOTNET_ROSLYN_SERVER_PATH; - - if (serverPath) { - _channel.appendLine(`Using server path override from DOTNET_ROSLYN_SERVER_PATH: ${serverPath}`); - } else { - serverPath = commonOptions.serverPath; - if (!serverPath) { - // Option not set, use the path from the extension. - serverPath = getInstalledServerPath(platformInfo); - } - } - - if (!fs.existsSync(serverPath)) { - throw new Error(`Cannot find language server in path '${serverPath}'`); - } - - return serverPath; -} - -function getInstalledServerPath(platformInfo: PlatformInformation): string { - const clientRoot = __dirname; - const serverFilePath = path.join(clientRoot, '..', '.roslyn', 'Microsoft.CodeAnalysis.LanguageServer'); - - let extension = ''; - if (platformInfo.isWindows()) { - extension = '.exe'; - } else if (platformInfo.isMacOS()) { - // MacOS executables must be signed with codesign. Currently all Roslyn server executables are built on windows - // and therefore dotnet publish does not automatically sign them. - // Tracking bug - https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1767519/ - extension = '.dll'; - } - - let pathWithExtension = `${serverFilePath}${extension}`; - if (!fs.existsSync(pathWithExtension)) { - // We might be running a platform neutral vsix which has no executable, instead we run the dll directly. - pathWithExtension = `${serverFilePath}.dll`; - } - - return pathWithExtension; -} - // VS code will have a default session id when running under tests. Since we may still // report telemetry, we need to give a unique session id instead of the default value. function getSessionId(): string { @@ -1225,7 +1097,3 @@ function getSessionId(): string { return sessionId; } - -export function isString(value: any): value is string { - return typeof value === 'string' || value instanceof String; -} diff --git a/src/lsptoolshost/roslynProtocol.ts b/src/lsptoolshost/server/roslynProtocol.ts similarity index 99% rename from src/lsptoolshost/roslynProtocol.ts rename to src/lsptoolshost/server/roslynProtocol.ts index bea4640c8..583d3b26f 100644 --- a/src/lsptoolshost/roslynProtocol.ts +++ b/src/lsptoolshost/server/roslynProtocol.ts @@ -6,7 +6,7 @@ import { Command } from 'vscode'; import * as lsp from 'vscode-languageserver-protocol'; import { CodeAction, TextDocumentRegistrationOptions } from 'vscode-languageserver-protocol'; -import { ProjectConfigurationMessage } from '../shared/projectConfiguration'; +import { ProjectConfigurationMessage } from '../../shared/projectConfiguration'; export interface VSProjectContextList { _vs_projectContexts: VSProjectContext[]; diff --git a/src/lsptoolshost/server/serverCommands.ts b/src/lsptoolshost/server/serverCommands.ts new file mode 100644 index 000000000..f9637211a --- /dev/null +++ b/src/lsptoolshost/server/serverCommands.ts @@ -0,0 +1,156 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; +import { UriConverter } from '../utils/uriConverter'; +import * as languageClient from 'vscode-languageclient/node'; + +/** + * Register server -> client commands as well as commands that drive the Roslyn Language Server. + */ +export function registerServerCommands( + context: vscode.ExtensionContext, + languageServer: RoslynLanguageServer, + outputChannel: vscode.LogOutputChannel +) { + // It is very important to be careful about the types used as parameters for these command callbacks. + // If the arguments are coming from the server as json, it is NOT appropriate to use type definitions + // from the normal vscode API (e.g. vscode.Location) as input parameters. + // + // This is because at runtime the json objects do not contain the expected prototypes that the vscode types + // have and will fail 'instanceof' checks that are sprinkled throught the vscode APIs. + // + // Instead, we define inputs from the server using the LSP type definitions as those have no prototypes + // so we don't accidentally pass them directly into vscode APIs. + context.subscriptions.push(vscode.commands.registerCommand('roslyn.client.peekReferences', peekReferencesCallback)); + context.subscriptions.push( + vscode.commands.registerCommand( + 'roslyn.client.completionComplexEdit', + async (textDocument, textEdit, isSnippetString, newOffset) => + completionComplexEdit(textDocument, textEdit, isSnippetString, newOffset, outputChannel) + ) + ); + context.subscriptions.push( + vscode.commands.registerCommand('dotnet.restartServer', async () => restartServer(languageServer)) + ); +} + +/** + * Callback for code lens commands. Executes a references request via the VSCode command + * which will call into the LSP server to get the data. Then calls the VSCode command to display the result. + * @param uriStr The uri containing the location to find references for. + * @param serverPosition The position json object to execute the find references request. + */ +async function peekReferencesCallback(uriStr: string, serverPosition: languageClient.Position): Promise { + const uri = UriConverter.deserialize(uriStr); + + // Convert the json position object into the corresponding vscode position type. + const vscodeApiPosition = new vscode.Position(serverPosition.line, serverPosition.character); + const references: vscode.Location[] = await vscode.commands.executeCommand( + 'vscode.executeReferenceProvider', + uri, + vscodeApiPosition + ); + + if (references && Array.isArray(references)) { + // The references could come back after the document has moved to a new state (that may not even contain the position). + // This is fine - the VSCode API is resilient to that scenario and will not crash. + await vscode.commands.executeCommand('editor.action.showReferences', uri, vscodeApiPosition, references); + } +} + +/** + * Callback after a completion item with complex edit is committed. The change needs to be made outside completion resolve + * handling + * + * IMPORTANT: @see RazorCompletionItemProvider.resolveCompletionItem matches the arguments for this commands + * so it can remap correctly in razor files. Any updates to this function signature requires updates there as well. + * + * @param uriStr The uri containing the location of the document where the completion item was committed in. + * @param textEdits The additional complex edit for the committed completion item. + * @param isSnippetString Indicates if the TextEdit contains a snippet string. + * @param newPosition The offset for new cursor position. -1 if the edit has not specified one. + */ +async function completionComplexEdit( + textDocument: languageClient.TextDocumentIdentifier, + textEdit: vscode.TextEdit, + isSnippetString: boolean, + newOffset: number, + outputChannel: vscode.LogOutputChannel +): Promise { + const componentName = '[roslyn.client.completionComplexEdit]'; + + // Find TextDocument, opening if needed. + const uri = UriConverter.deserialize(textDocument.uri); + const document = await vscode.workspace.openTextDocument(uri); + if (document === undefined) { + outputAndThrow(outputChannel, `${componentName} Can't open document with path: '${textDocument.uri}'`); + } + + // Use editor if we need to deal with selection or snippets. + let editor: vscode.TextEditor | undefined = undefined; + if (isSnippetString || newOffset >= 0) { + editor = await vscode.window.showTextDocument(document); + if (editor === undefined) { + outputAndThrow( + outputChannel, + `${componentName} Editor unavailable for document with path: '${textDocument.uri}'` + ); + } + } + + const newRange = document.validateRange( + new vscode.Range( + textEdit.range.start.line, + textEdit.range.start.character, + textEdit.range.end.line, + textEdit.range.end.character + ) + ); + + // HACK: + // ApplyEdit would fail the first time it's called when an item was committed with text modifying commit char (e.g. space, '(', etc.) + // so we retry a couple time here as a tempory workaround. We need to either figure our the reason of the failure, and/or try the + // approach of sending another edit request to server with updated document. + let success = false; + for (let i = 0; i < 3; i++) { + if (isSnippetString) { + editor!.selection = new vscode.Selection(newRange.start, newRange.end); + success = await editor!.insertSnippet(new vscode.SnippetString(textEdit.newText)); + } else { + const edit = new vscode.WorkspaceEdit(); + const newTextEdit = vscode.TextEdit.replace(newRange, textEdit.newText); + edit.set(document.uri, [newTextEdit]); + success = await vscode.workspace.applyEdit(edit); + + if (success && newOffset >= 0) { + const newPosition = document.positionAt(newOffset); + editor!.selections = [new vscode.Selection(newPosition, newPosition)]; + } + } + + if (success) { + return; + } + } + + if (!success) { + outputAndThrow( + outputChannel, + `${componentName} ${isSnippetString ? 'TextEditor.insertSnippet' : 'workspace.applyEdit'} failed.` + ); + } +} + +function outputAndThrow(outputChannel: vscode.LogOutputChannel, message: string): void { + outputChannel.show(); + outputChannel.error(message); + throw new Error(message); +} + +async function restartServer(languageServer: RoslynLanguageServer): Promise { + await languageServer.restart(); +} diff --git a/src/lsptoolshost/services/brokeredServicesHosting.ts b/src/lsptoolshost/serviceBroker/brokeredServicesHosting.ts similarity index 96% rename from src/lsptoolshost/services/brokeredServicesHosting.ts rename to src/lsptoolshost/serviceBroker/brokeredServicesHosting.ts index 5fcf8d394..f0da662ab 100644 --- a/src/lsptoolshost/services/brokeredServicesHosting.ts +++ b/src/lsptoolshost/serviceBroker/brokeredServicesHosting.ts @@ -11,7 +11,7 @@ import { ServiceMoniker, ServiceRegistration, } from '@microsoft/servicehub-framework'; -import Descriptors from './descriptors'; +import Descriptors from '../solutionSnapshot/descriptors'; export class CSharpExtensionServiceBroker extends GlobalBrokeredServiceContainer { registerExternalServices(...monikers: ServiceMoniker[]) { diff --git a/src/lsptoolshost/services/ISolutionSnapshotProvider.ts b/src/lsptoolshost/solutionSnapshot/ISolutionSnapshotProvider.ts similarity index 100% rename from src/lsptoolshost/services/ISolutionSnapshotProvider.ts rename to src/lsptoolshost/solutionSnapshot/ISolutionSnapshotProvider.ts diff --git a/src/lsptoolshost/services/descriptors.ts b/src/lsptoolshost/solutionSnapshot/descriptors.ts similarity index 100% rename from src/lsptoolshost/services/descriptors.ts rename to src/lsptoolshost/solutionSnapshot/descriptors.ts diff --git a/src/lsptoolshost/services/solutionSnapshotProvider.ts b/src/lsptoolshost/solutionSnapshot/solutionSnapshotProvider.ts similarity index 92% rename from src/lsptoolshost/services/solutionSnapshotProvider.ts rename to src/lsptoolshost/solutionSnapshot/solutionSnapshotProvider.ts index 41908edb8..f346bed7a 100644 --- a/src/lsptoolshost/services/solutionSnapshotProvider.ts +++ b/src/lsptoolshost/solutionSnapshot/solutionSnapshotProvider.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { RoslynLanguageServer } from '../roslynLanguageServer'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; import { ISolutionSnapshotProvider, SolutionSnapshotId } from './ISolutionSnapshotProvider'; /** diff --git a/src/lsptoolshost/unitTesting.ts b/src/lsptoolshost/testing/unitTesting.ts similarity index 95% rename from src/lsptoolshost/unitTesting.ts rename to src/lsptoolshost/testing/unitTesting.ts index 24e812c65..d12e0ca47 100644 --- a/src/lsptoolshost/unitTesting.ts +++ b/src/lsptoolshost/testing/unitTesting.ts @@ -7,12 +7,12 @@ import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; import * as languageClient from 'vscode-languageclient/node'; -import { RoslynLanguageServer } from './roslynLanguageServer'; -import { RunTestsParams, RunTestsPartialResult, RunTestsRequest, TestProgress } from './roslynProtocol'; -import { commonOptions } from '../shared/options'; -import { UriConverter } from './uriConverter'; -import { showErrorMessage } from '../shared/observers/utils/showMessage'; -import { getCSharpDevKit } from '../utils/getCSharpDevKit'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; +import { RunTestsParams, RunTestsPartialResult, RunTestsRequest, TestProgress } from '../server/roslynProtocol'; +import { commonOptions } from '../../shared/options'; +import { UriConverter } from '../utils/uriConverter'; +import { showErrorMessage } from '../../shared/observers/utils/showMessage'; +import { getCSharpDevKit } from '../../utils/getCSharpDevKit'; export function registerUnitTestingCommands(context: vscode.ExtensionContext, languageServer: RoslynLanguageServer) { if (getCSharpDevKit()) { diff --git a/src/lsptoolshost/utils/combineDocumentSelectors.ts b/src/lsptoolshost/utils/combineDocumentSelectors.ts new file mode 100644 index 000000000..14ad132b6 --- /dev/null +++ b/src/lsptoolshost/utils/combineDocumentSelectors.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +export function combineDocumentSelectors(...selectors: vscode.DocumentSelector[]): vscode.DocumentSelector { + return selectors.reduce<(string | vscode.DocumentFilter)[]>((acc, selector) => acc.concat(selector), []); +} diff --git a/src/lsptoolshost/serverStateChange.ts b/src/lsptoolshost/utils/isString.ts similarity index 60% rename from src/lsptoolshost/serverStateChange.ts rename to src/lsptoolshost/utils/isString.ts index 0db11810e..acf6c629a 100644 --- a/src/lsptoolshost/serverStateChange.ts +++ b/src/lsptoolshost/utils/isString.ts @@ -3,14 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export enum ServerState { - Stopped = 0, - Started = 1, - ProjectInitializationStarted = 2, - ProjectInitializationComplete = 3, -} - -export interface ServerStateChangeEvent { - state: ServerState; - workspaceLabel: string; +export function isString(value: any): value is string { + return typeof value === 'string' || value instanceof String; } diff --git a/src/lsptoolshost/uriConverter.ts b/src/lsptoolshost/utils/uriConverter.ts similarity index 92% rename from src/lsptoolshost/uriConverter.ts rename to src/lsptoolshost/utils/uriConverter.ts index 88c648599..24d19ff79 100644 --- a/src/lsptoolshost/uriConverter.ts +++ b/src/lsptoolshost/utils/uriConverter.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { CSharpProjectedDocumentContentProvider } from '../razor/src/csharp/csharpProjectedDocumentContentProvider'; +import { CSharpProjectedDocumentContentProvider } from '../../razor/src/csharp/csharpProjectedDocumentContentProvider'; export class UriConverter { public static serialize(uri: vscode.Uri): string { diff --git a/src/lsptoolshost/miscellaneousFileNotifier.ts b/src/lsptoolshost/workspace/miscellaneousFileNotifier.ts similarity index 89% rename from src/lsptoolshost/miscellaneousFileNotifier.ts rename to src/lsptoolshost/workspace/miscellaneousFileNotifier.ts index 100f73c17..32d6bce5e 100644 --- a/src/lsptoolshost/miscellaneousFileNotifier.ts +++ b/src/lsptoolshost/workspace/miscellaneousFileNotifier.ts @@ -5,10 +5,10 @@ import * as vscode from 'vscode'; import * as crypto from 'crypto'; -import { RoslynLanguageServer } from './roslynLanguageServer'; -import { ActionOption, showWarningMessage } from '../shared/observers/utils/showMessage'; -import { ServerState } from './serverStateChange'; -import { languageServerOptions } from '../shared/options'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; +import { ActionOption, showWarningMessage } from '../../shared/observers/utils/showMessage'; +import { languageServerOptions } from '../../shared/options'; +import { ServerState } from '../server/languageServerEvents'; const SuppressMiscellaneousFilesToastsOption = 'dotnet.server.suppressMiscellaneousFilesToasts'; const NotifiedDocuments = new Set(); diff --git a/src/lsptoolshost/workspace/workspaceCommands.ts b/src/lsptoolshost/workspace/workspaceCommands.ts new file mode 100644 index 000000000..c2aeea5a2 --- /dev/null +++ b/src/lsptoolshost/workspace/workspaceCommands.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { RoslynLanguageServer } from '../server/roslynLanguageServer'; +import { createLaunchTargetForSolution } from '../../shared/launchTarget'; +import { getCSharpDevKit } from '../../utils/getCSharpDevKit'; + +/** + * Register commands that drive the workspace. + */ +export function registerWorkspaceCommands(context: vscode.ExtensionContext, languageServer: RoslynLanguageServer) { + if (!getCSharpDevKit()) { + context.subscriptions.push( + vscode.commands.registerCommand('dotnet.openSolution', async () => openSolution(languageServer)) + ); + } +} + +async function openSolution(languageServer: RoslynLanguageServer): Promise { + if (!vscode.workspace.workspaceFolders) { + return undefined; + } + + const solutionFiles = await vscode.workspace.findFiles('**/*.{sln,slnf}'); + const launchTargets = solutionFiles.map(createLaunchTargetForSolution); + const launchTarget = await vscode.window.showQuickPick(launchTargets, { + matchOnDescription: true, + placeHolder: `Select solution file`, + }); + + if (launchTarget) { + const uri = vscode.Uri.file(launchTarget.target); + await languageServer.openSolution(uri); + return uri; + } +} diff --git a/src/lsptoolshost/workspace/workspaceStatus.ts b/src/lsptoolshost/workspace/workspaceStatus.ts new file mode 100644 index 000000000..b51ca172b --- /dev/null +++ b/src/lsptoolshost/workspace/workspaceStatus.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { RoslynLanguageServerEvents, ServerState } from '../server/languageServerEvents'; +import { combineDocumentSelectors } from '../utils/combineDocumentSelectors'; +import { languageServerOptions } from '../../shared/options'; + +export class WorkspaceStatus { + static createStatusItem(context: vscode.ExtensionContext, languageServerEvents: RoslynLanguageServerEvents) { + const documentSelector = combineDocumentSelectors(languageServerOptions.documentSelector); + const openSolutionCommand = { + command: 'dotnet.openSolution', + title: vscode.l10n.t('Open solution'), + }; + const restartServerCommand = { + command: 'dotnet.restartServer', + title: vscode.l10n.t('Restart server'), + }; + + const item = vscode.languages.createLanguageStatusItem('csharp.workspaceStatus', documentSelector); + item.name = vscode.l10n.t('C# Workspace Status'); + item.severity = vscode.LanguageStatusSeverity.Error; + item.command = openSolutionCommand; + context.subscriptions.push(item); + + languageServerEvents.onServerStateChange((e) => { + item.text = e.workspaceLabel; + item.busy = e.state === ServerState.ProjectInitializationStarted; + item.severity = + e.state === ServerState.Stopped + ? vscode.LanguageStatusSeverity.Error + : vscode.LanguageStatusSeverity.Information; + item.command = e.state === ServerState.Stopped ? restartServerCommand : openSolutionCommand; + }); + } +} diff --git a/src/main.ts b/src/main.ts index 2f2dd84fd..9eb06d7a6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -22,27 +22,27 @@ import IInstallDependencies from './packageManager/IInstallDependencies'; import { installRuntimeDependencies } from './installRuntimeDependencies'; import { isValidDownload } from './packageManager/isValidDownload'; import { getDotnetPackApi } from './dotnetPack'; -import { RoslynLanguageServer, activateRoslynLanguageServer } from './lsptoolshost/roslynLanguageServer'; +import { RoslynLanguageServer } from './lsptoolshost/server/roslynLanguageServer'; import { MigrateOptions } from './shared/migrateOptions'; -import { getBrokeredServiceContainer } from './lsptoolshost/services/brokeredServicesHosting'; +import { getBrokeredServiceContainer } from './lsptoolshost/serviceBroker/brokeredServicesHosting'; import { CSharpDevKitExports } from './csharpDevKitExports'; -import Descriptors from './lsptoolshost/services/descriptors'; +import Descriptors from './lsptoolshost/solutionSnapshot/descriptors'; import { GlobalBrokeredServiceContainer } from '@microsoft/servicehub-framework'; import { CSharpExtensionExports, OmnisharpExtensionExports } from './csharpExtensionExports'; import { csharpDevkitExtensionId, getCSharpDevKit } from './utils/getCSharpDevKit'; import { BlazorDebugConfigurationProvider } from './razor/src/blazorDebug/blazorDebugConfigurationProvider'; -import { RoslynLanguageServerExport } from './lsptoolshost/roslynLanguageServerExportChannel'; -import { RoslynLanguageServerEvents } from './lsptoolshost/languageServerEvents'; -import { ServerState } from './lsptoolshost/serverStateChange'; -import { SolutionSnapshotProvider } from './lsptoolshost/services/solutionSnapshotProvider'; +import { SolutionSnapshotProvider } from './lsptoolshost/solutionSnapshot/solutionSnapshotProvider'; import { commonOptions, languageServerOptions, omnisharpOptions, razorOptions } from './shared/options'; -import { BuildResultDiagnostics } from './lsptoolshost/services/buildResultReporterService'; +import { BuildResultDiagnostics } from './lsptoolshost/diagnostics/buildResultReporterService'; import { debugSessionTracker } from './coreclrDebug/provisionalDebugSessionTracker'; -import { getComponentFolder } from './lsptoolshost/builtInComponents'; import { activateOmniSharpLanguageServer, ActivationResult } from './omnisharp/omnisharpLanguageServer'; import { ActionOption, showErrorMessage } from './shared/observers/utils/showMessage'; import { lt } from 'semver'; import { TelemetryEventNames } from './shared/telemetryEventNames'; +import { RoslynLanguageServerEvents, ServerState } from './lsptoolshost/server/languageServerEvents'; +import { activateRoslynLanguageServer } from './lsptoolshost/activate'; +import { RoslynLanguageServerExport } from './lsptoolshost/extensions/roslynLanguageServerExportChannel'; +import { getComponentFolder } from './lsptoolshost/extensions/builtInComponents'; export async function activate( context: vscode.ExtensionContext diff --git a/src/razor/src/codeActions/codeActionsHandler.ts b/src/razor/src/codeActions/codeActionsHandler.ts index c91446023..8af500ba7 100644 --- a/src/razor/src/codeActions/codeActionsHandler.ts +++ b/src/razor/src/codeActions/codeActionsHandler.ts @@ -10,10 +10,10 @@ import { RazorLanguageServerClient } from '../razorLanguageServerClient'; import { RazorLogger } from '../razorLogger'; import { SerializableDelegatedCodeActionParams } from './serializableDelegatedCodeActionParams'; import { LanguageKind } from '../rpc/languageKind'; -import { UriConverter } from '../../../lsptoolshost/uriConverter'; +import { UriConverter } from '../../../lsptoolshost/utils/uriConverter'; import { SerializableRazorResolveCodeActionParams } from './serializableRazorResolveCodeActionParams'; import { RazorDocumentSynchronizer } from '../document/razorDocumentSynchronizer'; -import { provideCodeActionsCommand, resolveCodeActionCommand } from '../../../lsptoolshost/razorCommands'; +import { provideCodeActionsCommand, resolveCodeActionCommand } from '../../../lsptoolshost/razor/razorCommands'; export class CodeActionsHandler { private static readonly provideCodeActionsEndpoint = 'razor/provideCodeActions'; diff --git a/src/razor/src/completion/completionHandler.ts b/src/razor/src/completion/completionHandler.ts index 48247a16a..44d4a1596 100644 --- a/src/razor/src/completion/completionHandler.ts +++ b/src/razor/src/completion/completionHandler.ts @@ -18,7 +18,7 @@ import { RequestType, TextEdit, } from 'vscode-languageclient'; -import { provideCompletionsCommand, resolveCompletionsCommand } from '../../../lsptoolshost/razorCommands'; +import { provideCompletionsCommand, resolveCompletionsCommand } from '../../../lsptoolshost/razor/razorCommands'; import { RazorDocumentManager } from '../document/razorDocumentManager'; import { RazorDocumentSynchronizer } from '../document/razorDocumentSynchronizer'; import { RazorLanguageServerClient } from '../razorLanguageServerClient'; @@ -26,7 +26,7 @@ import { RazorLogger } from '../razorLogger'; import { SerializableDelegatedCompletionParams } from './serializableDelegatedCompletionParams'; import { SerializableDelegatedCompletionItemResolveParams } from './serializableDelegatedCompletionItemResolveParams'; import { LanguageKind } from '../rpc/languageKind'; -import { UriConverter } from '../../../lsptoolshost/uriConverter'; +import { UriConverter } from '../../../lsptoolshost/utils/uriConverter'; import { SerializableTextEdit } from '../rpc/serializableTextEdit'; import { CSharpProjectedDocument } from '../csharp/csharpProjectedDocument'; import { IProjectedDocument } from '../projection/IProjectedDocument'; diff --git a/src/razor/src/diagnostics/razorDiagnosticHandler.ts b/src/razor/src/diagnostics/razorDiagnosticHandler.ts index 8c63228f4..d734681b0 100644 --- a/src/razor/src/diagnostics/razorDiagnosticHandler.ts +++ b/src/razor/src/diagnostics/razorDiagnosticHandler.ts @@ -7,12 +7,12 @@ import * as vscode from 'vscode'; import { DocumentDiagnosticReport, DocumentDiagnosticParams, RequestType } from 'vscode-languageclient'; import { RazorLanguageServerClient } from '../razorLanguageServerClient'; import { RazorDocumentManager } from '../document/razorDocumentManager'; -import { UriConverter } from '../../../lsptoolshost/uriConverter'; +import { UriConverter } from '../../../lsptoolshost/utils/uriConverter'; import { RazorLanguageServiceClient } from '../razorLanguageServiceClient'; import { RazorLanguageFeatureBase } from '../razorLanguageFeatureBase'; import { RazorDocumentSynchronizer } from '../document/razorDocumentSynchronizer'; import { RazorLogger } from '../razorLogger'; -import { roslynPullDiagnosticCommand } from '../../../lsptoolshost/razorCommands'; +import { roslynPullDiagnosticCommand } from '../../../lsptoolshost/razor/razorCommands'; export class RazorDiagnosticHandler extends RazorLanguageFeatureBase { private static readonly razorPullDiagnosticsCommand = 'razor/csharpPullDiagnostics'; diff --git a/src/razor/src/document/razorDocumentManager.ts b/src/razor/src/document/razorDocumentManager.ts index b90e303fd..3d5adf087 100644 --- a/src/razor/src/document/razorDocumentManager.ts +++ b/src/razor/src/document/razorDocumentManager.ts @@ -17,7 +17,7 @@ import { IRazorDocumentChangeEvent } from './IRazorDocumentChangeEvent'; import { IRazorDocumentManager } from './IRazorDocumentManager'; import { RazorDocumentChangeKind } from './razorDocumentChangeKind'; import { createDocument } from './razorDocumentFactory'; -import { razorInitializeCommand } from '../../../lsptoolshost/razorCommands'; +import { razorInitializeCommand } from '../../../lsptoolshost/razor/razorCommands'; import { PlatformInformation } from '../../../shared/platform'; import { generateUuid } from 'vscode-languageclient/lib/common/utils/uuid'; diff --git a/src/razor/src/dynamicFile/dynamicFileInfoHandler.ts b/src/razor/src/dynamicFile/dynamicFileInfoHandler.ts index 62300f0f5..7208c637b 100644 --- a/src/razor/src/dynamicFile/dynamicFileInfoHandler.ts +++ b/src/razor/src/dynamicFile/dynamicFileInfoHandler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { UriConverter } from '../../../lsptoolshost/uriConverter'; +import { UriConverter } from '../../../lsptoolshost/utils/uriConverter'; import { RazorDocumentManager } from '../document/razorDocumentManager'; import { RazorLogger } from '../razorLogger'; import { ProvideDynamicFileParams } from './provideDynamicFileParams'; diff --git a/src/razor/src/extension.ts b/src/razor/src/extension.ts index fde0a2039..7e2ddc43e 100644 --- a/src/razor/src/extension.ts +++ b/src/razor/src/extension.ts @@ -42,14 +42,14 @@ import { RazorDiagnosticHandler } from './diagnostics/razorDiagnosticHandler'; import { RazorSimplifyMethodHandler } from './simplify/razorSimplifyMethodHandler'; import TelemetryReporter from '@vscode/extension-telemetry'; import { CSharpDevKitExports } from '../../csharpDevKitExports'; -import { DotnetRuntimeExtensionResolver } from '../../lsptoolshost/dotnetRuntimeExtensionResolver'; +import { DotnetRuntimeExtensionResolver } from '../../lsptoolshost/dotnetRuntime/dotnetRuntimeExtensionResolver'; import { PlatformInformation } from '../../shared/platform'; import { RazorLanguageServerOptions } from './razorLanguageServerOptions'; import { resolveRazorLanguageServerOptions } from './razorLanguageServerOptionsResolver'; import { RazorFormatNewFileHandler } from './formatNewFile/razorFormatNewFileHandler'; import { InlayHintHandler } from './inlayHint/inlayHintHandler'; import { InlayHintResolveHandler } from './inlayHint/inlayHintResolveHandler'; -import { getComponentPaths } from '../../lsptoolshost/builtInComponents'; +import { getComponentPaths } from '../../lsptoolshost/extensions/builtInComponents'; import { BlazorDebugConfigurationProvider } from './blazorDebug/blazorDebugConfigurationProvider'; // We specifically need to take a reference to a particular instance of the vscode namespace, diff --git a/src/razor/src/formatNewFile/razorFormatNewFileHandler.ts b/src/razor/src/formatNewFile/razorFormatNewFileHandler.ts index 998cd618d..d89011c49 100644 --- a/src/razor/src/formatNewFile/razorFormatNewFileHandler.ts +++ b/src/razor/src/formatNewFile/razorFormatNewFileHandler.ts @@ -12,7 +12,7 @@ import { RazorLanguageFeatureBase } from '../razorLanguageFeatureBase'; import { RazorDocumentSynchronizer } from '../document/razorDocumentSynchronizer'; import { RazorLogger } from '../razorLogger'; import { SerializableFormatNewFileParams } from './serializableFormatNewFileParams'; -import { roslynFormatNewFileCommand } from '../../../lsptoolshost/razorCommands'; +import { roslynFormatNewFileCommand } from '../../../lsptoolshost/razor/razorCommands'; export class RazorFormatNewFileHandler extends RazorLanguageFeatureBase { private static readonly razorFormatNewFileCommand = 'razor/formatNewFile'; diff --git a/src/razor/src/inlayHint/inlayHintHandler.ts b/src/razor/src/inlayHint/inlayHintHandler.ts index 622f5fe31..5a4f6e514 100644 --- a/src/razor/src/inlayHint/inlayHintHandler.ts +++ b/src/razor/src/inlayHint/inlayHintHandler.ts @@ -9,8 +9,8 @@ import { RazorDocumentManager } from '../document/razorDocumentManager'; import { RazorLanguageServerClient } from '../razorLanguageServerClient'; import { RazorLogger } from '../razorLogger'; import { SerializableInlayHintParams } from './serializableInlayHintParams'; -import { provideInlayHintsCommand } from '../../../lsptoolshost/razorCommands'; -import { UriConverter } from '../../../lsptoolshost/uriConverter'; +import { provideInlayHintsCommand } from '../../../lsptoolshost/razor/razorCommands'; +import { UriConverter } from '../../../lsptoolshost/utils/uriConverter'; import { RazorDocumentSynchronizer } from '../document/razorDocumentSynchronizer'; export class InlayHintHandler { diff --git a/src/razor/src/inlayHint/inlayHintResolveHandler.ts b/src/razor/src/inlayHint/inlayHintResolveHandler.ts index d0d3b0f6f..264e437f3 100644 --- a/src/razor/src/inlayHint/inlayHintResolveHandler.ts +++ b/src/razor/src/inlayHint/inlayHintResolveHandler.ts @@ -9,7 +9,7 @@ import { RazorDocumentManager } from '../document/razorDocumentManager'; import { RazorLanguageServerClient } from '../razorLanguageServerClient'; import { RazorLogger } from '../razorLogger'; import { SerializableInlayHintResolveParams } from './serializableInlayHintResolveParams'; -import { resolveInlayHintCommand } from '../../../lsptoolshost/razorCommands'; +import { resolveInlayHintCommand } from '../../../lsptoolshost/razor/razorCommands'; export class InlayHintResolveHandler { private static readonly resolveInlayHint = 'razor/inlayHintResolve'; diff --git a/src/razor/src/simplify/razorSimplifyMethodHandler.ts b/src/razor/src/simplify/razorSimplifyMethodHandler.ts index 2c340e85c..59fca7967 100644 --- a/src/razor/src/simplify/razorSimplifyMethodHandler.ts +++ b/src/razor/src/simplify/razorSimplifyMethodHandler.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { RequestType, TextDocumentIdentifier } from 'vscode-languageclient'; import { RazorLanguageServerClient } from '../razorLanguageServerClient'; import { RazorDocumentManager } from '../document/razorDocumentManager'; -import { UriConverter } from '../../../lsptoolshost/uriConverter'; +import { UriConverter } from '../../../lsptoolshost/utils/uriConverter'; import { RazorLanguageServiceClient } from '../razorLanguageServiceClient'; import { RazorLanguageFeatureBase } from '../razorLanguageFeatureBase'; import { RazorDocumentSynchronizer } from '../document/razorDocumentSynchronizer'; @@ -15,7 +15,7 @@ import { RazorLogger } from '../razorLogger'; import { SerializableDelegatedSimplifyMethodParams } from './serializableDelegatedSimplifyMethodParams'; import SerializableSimplifyMethodParams from './serializableSimplifyMethodParams'; import { TextEdit } from 'vscode-html-languageservice'; -import { roslynSimplifyMethodCommand } from '../../../lsptoolshost/razorCommands'; +import { roslynSimplifyMethodCommand } from '../../../lsptoolshost/razor/razorCommands'; export class RazorSimplifyMethodHandler extends RazorLanguageFeatureBase { private static readonly razorSimplifyMethodCommand = 'razor/simplifyMethod'; diff --git a/src/shared/dotnetConfigurationProvider.ts b/src/shared/dotnetConfigurationProvider.ts index d07e5449a..cebce60a0 100644 --- a/src/shared/dotnetConfigurationProvider.ts +++ b/src/shared/dotnetConfigurationProvider.ts @@ -7,13 +7,13 @@ import * as fs from 'fs-extra'; import * as vscode from 'vscode'; import { IWorkspaceDebugInformationProvider, ProjectDebugInformation } from './IWorkspaceDebugInformationProvider'; import { AssetGenerator, AssetOperations, addTasksJsonIfNecessary, getBuildOperations } from './assets'; -import { getServiceBroker } from '../lsptoolshost/services/brokeredServicesHosting'; -import Descriptors from '../lsptoolshost/services/descriptors'; +import { getServiceBroker } from '../lsptoolshost/serviceBroker/brokeredServicesHosting'; +import Descriptors from '../lsptoolshost/solutionSnapshot/descriptors'; import { DotnetDebugConfigurationServiceErrorKind, IDotnetDebugConfigurationService, IDotnetDebugConfigurationServiceResult, -} from '../lsptoolshost/services/IDotnetDebugConfigurationService'; +} from '../lsptoolshost/debugger/IDotnetDebugConfigurationService'; import { DotnetWorkspaceConfigurationProvider } from './workspaceConfigurationProvider'; // User errors that can be shown to the user. diff --git a/tasks/projectPaths.ts b/tasks/projectPaths.ts index 3f74d1f76..ce44b6805 100644 --- a/tasks/projectPaths.ts +++ b/tasks/projectPaths.ts @@ -5,7 +5,7 @@ import * as path from 'path'; import { commandLineOptions } from './commandLineArguments'; -import { componentInfo } from '../src/lsptoolshost/builtInComponents'; +import { componentInfo } from '../src/lsptoolshost/extensions/builtInComponents'; export const rootPath = path.resolve(__dirname, '..'); diff --git a/test/lsptoolshost/integrationTests/buildDiagnostics.integration.test.ts b/test/lsptoolshost/integrationTests/buildDiagnostics.integration.test.ts index d05844cba..7d49a9167 100644 --- a/test/lsptoolshost/integrationTests/buildDiagnostics.integration.test.ts +++ b/test/lsptoolshost/integrationTests/buildDiagnostics.integration.test.ts @@ -6,7 +6,10 @@ import * as vscode from 'vscode'; import { describe, test, expect, beforeAll, afterAll, beforeEach, afterEach } from '@jest/globals'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; -import { AnalysisSetting, BuildDiagnosticsService } from '../../../src/lsptoolshost/buildDiagnosticsService'; +import { + AnalysisSetting, + BuildDiagnosticsService, +} from '../../../src/lsptoolshost/diagnostics/buildDiagnosticsService'; import * as integrationHelpers from './integrationHelpers'; import path from 'path'; describe(`Build and live diagnostics dedupe`, () => { diff --git a/test/lsptoolshost/integrationTests/documentDiagnostics.integration.test.ts b/test/lsptoolshost/integrationTests/documentDiagnostics.integration.test.ts index dcfaeb39e..6221bbd4a 100644 --- a/test/lsptoolshost/integrationTests/documentDiagnostics.integration.test.ts +++ b/test/lsptoolshost/integrationTests/documentDiagnostics.integration.test.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { describe, test, beforeAll, afterAll, expect, beforeEach, afterEach } from '@jest/globals'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; -import { AnalysisSetting } from '../../../src/lsptoolshost/buildDiagnosticsService'; +import { AnalysisSetting } from '../../../src/lsptoolshost/diagnostics/buildDiagnosticsService'; import path from 'path'; import { getCode, setBackgroundAnalysisScopes, waitForExpectedDiagnostics } from './diagnosticsHelpers'; import { diff --git a/test/lsptoolshost/integrationTests/integrationHelpers.ts b/test/lsptoolshost/integrationTests/integrationHelpers.ts index 2a5c686d2..a7d3b89eb 100644 --- a/test/lsptoolshost/integrationTests/integrationHelpers.ts +++ b/test/lsptoolshost/integrationTests/integrationHelpers.ts @@ -8,7 +8,7 @@ import * as path from 'path'; import * as semver from 'semver'; import { CSharpExtensionExports } from '../../../src/csharpExtensionExports'; import { existsSync } from 'fs'; -import { ServerState } from '../../../src/lsptoolshost/serverStateChange'; +import { ServerState } from '../../../src/lsptoolshost/server/languageServerEvents'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; import { EOL, platform } from 'os'; import { describe, expect, test } from '@jest/globals'; diff --git a/test/lsptoolshost/integrationTests/unitTests.integration.test.ts b/test/lsptoolshost/integrationTests/unitTests.integration.test.ts index ce7ccf03b..8753a6560 100644 --- a/test/lsptoolshost/integrationTests/unitTests.integration.test.ts +++ b/test/lsptoolshost/integrationTests/unitTests.integration.test.ts @@ -14,7 +14,7 @@ import { getCodeLensesAsync, openFileInWorkspaceAsync, } from './integrationHelpers'; -import { TestProgress } from '../../../src/lsptoolshost/roslynProtocol'; +import { TestProgress } from '../../../src/lsptoolshost/server/roslynProtocol'; describeIfCSharp(`Unit Testing Tests`, () => { beforeAll(async () => { diff --git a/test/lsptoolshost/integrationTests/workspaceDiagnostics.integration.test.ts b/test/lsptoolshost/integrationTests/workspaceDiagnostics.integration.test.ts index 234f87a4c..09fb87894 100644 --- a/test/lsptoolshost/integrationTests/workspaceDiagnostics.integration.test.ts +++ b/test/lsptoolshost/integrationTests/workspaceDiagnostics.integration.test.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { describe, test, expect, beforeAll, afterAll } from '@jest/globals'; import testAssetWorkspace from './testAssets/testAssetWorkspace'; -import { AnalysisSetting } from '../../../src/lsptoolshost/buildDiagnosticsService'; +import { AnalysisSetting } from '../../../src/lsptoolshost/diagnostics/buildDiagnosticsService'; import { getCode, setBackgroundAnalysisScopes, waitForExpectedDiagnostics } from './diagnosticsHelpers'; import { activateCSharpExtension, describeIfCSharp } from './integrationHelpers'; diff --git a/test/lsptoolshost/unitTests/configurationMiddleware.test.ts b/test/lsptoolshost/unitTests/configurationMiddleware.test.ts index 46b9f41d9..11b4f764b 100644 --- a/test/lsptoolshost/unitTests/configurationMiddleware.test.ts +++ b/test/lsptoolshost/unitTests/configurationMiddleware.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { readFileSync } from 'fs'; -import { convertServerOptionNameToClientConfigurationName } from '../../../src/lsptoolshost/optionNameConverter'; +import { convertServerOptionNameToClientConfigurationName } from '../../../src/lsptoolshost/options/optionNameConverter'; import { describe, test, expect } from '@jest/globals'; const editorBehaviorSection = 1; diff --git a/test/lsptoolshost/unitTests/languageServerConfigChangeObserver.test.ts b/test/lsptoolshost/unitTests/languageServerConfigChangeObserver.test.ts index e8bf2928d..cea6af83a 100644 --- a/test/lsptoolshost/unitTests/languageServerConfigChangeObserver.test.ts +++ b/test/lsptoolshost/unitTests/languageServerConfigChangeObserver.test.ts @@ -5,7 +5,7 @@ import { timeout } from 'rxjs/operators'; import { from as observableFrom, Subject, BehaviorSubject } from 'rxjs'; -import { registerLanguageServerOptionChanges } from '../../../src/lsptoolshost/optionChanges'; +import { registerLanguageServerOptionChanges } from '../../../src/lsptoolshost/options/optionChanges'; import { describe, beforeEach, test, expect } from '@jest/globals'; import * as vscode from 'vscode'; From 889a7c8e7e3bbb41f9e4f11ab3f383ebb243b473 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Thu, 6 Feb 2025 18:05:30 -0800 Subject: [PATCH 16/20] Update to Roslyn main version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d22b0e0ef..b5d4547cc 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ } }, "defaults": { - "roslyn": "4.14.0-1.25078.4", + "roslyn": "4.14.0-2.25106.12", "omniSharp": "1.39.12", "razor": "9.0.0-preview.25064.4", "razorOmnisharp": "7.0.0-preview.23363.1", From 520b5b1f40d2160f7a6f232e508f9bfbbc170d0d Mon Sep 17 00:00:00 2001 From: "REDMOND\\anchimna" Date: Fri, 7 Feb 2025 12:46:17 -0800 Subject: [PATCH 17/20] Update xamltools --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 97fdafaa8..f15dc7684 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "omniSharp": "1.39.12", "razor": "9.0.0-preview.25073.1", "razorOmnisharp": "7.0.0-preview.23363.1", - "xamlTools": "17.14.35730.156" + "xamlTools": "17.14.35807.11" }, "main": "./dist/extension", "l10n": "./l10n", From ad2168607c83b9c7ac4b74c8f5dc847563db6274 Mon Sep 17 00:00:00 2001 From: David Barbet Date: Fri, 7 Feb 2025 12:48:07 -0800 Subject: [PATCH 18/20] Update changelog before snap --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3965f0972..b80d92419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ - Debug from .csproj and .sln [#5876](/~https://github.com/dotnet/vscode-csharp/issues/5876) # 2.65.x +* Update Roslyn to run on .NET 9 (PR: [#7946](/~https://github.com/dotnet/vscode-csharp/pull/7946)) +* Update Roslyn to 4.14.0-2.25106.12 (PR: [#7969](/~https://github.com/dotnet/vscode-csharp/pull/7969)) + * Maintain whitespace when converting to switch expression (PR: [#77083](/~https://github.com/dotnet/roslyn/pull/77083)) + * Fix extra whitespace insertion for completion text edits (PR: [#77071](/~https://github.com/dotnet/roslyn/pull/77071)) + * Realize less of the syntax tree during AbstractSemanticModelReuseLanguageService.GetPreviousBodyNode (PR: [#77032](/~https://github.com/dotnet/roslyn/pull/77032)) + * Fix issue loading project with relative path globs (PR: [#76961](/~https://github.com/dotnet/roslyn/pull/76961)) + * Fix: Ensure DOTNET_ROOT is reset user defined value during test execution (PR: [#76819](/~https://github.com/dotnet/roslyn/pull/76819)) + * Update Roslyn LSP server to target .NET 9 (PR: [#76938](/~https://github.com/dotnet/roslyn/pull/76938)) + * Update ICSharpCode.Decompiler to 8.2.0.7535 (PR: [#71837](/~https://github.com/dotnet/roslyn/pull/71837)) + * Reduce CPU costs under AnalyzerExecutor.ExecuteSyntaxNodeActions (PR: [#76894](/~https://github.com/dotnet/roslyn/pull/76894)) +* Add code snippets for C# expression-bodied properties (PR: [#5683](/~https://github.com/dotnet/vscode-csharp/pull/5683)) +* Sync whitespace options even when detectIndentation is on (PR: [#7965](/~https://github.com/dotnet/vscode-csharp/pull/7965)) +* Bump Razor (PR: [#7940](/~https://github.com/dotnet/vscode-csharp/pull/7940)) # 2.64.x * Bump xamlTools to 17.14.35730.156 (PR: [#7932](/~https://github.com/dotnet/vscode-csharp/pull/7941)) From 05347a538e8a65dbd6ee3cb838094ea0f9099a1c Mon Sep 17 00:00:00 2001 From: "REDMOND\\anchimna" Date: Fri, 7 Feb 2025 12:50:17 -0800 Subject: [PATCH 19/20] Update Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3965f0972..a9fc308cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Debug from .csproj and .sln [#5876](/~https://github.com/dotnet/vscode-csharp/issues/5876) # 2.65.x +Bump xamltools to 17.14.35807.11(PR: [#7976]( /~https://github.com/dotnet/vscode-csharp/pull/7976)) # 2.64.x * Bump xamlTools to 17.14.35730.156 (PR: [#7932](/~https://github.com/dotnet/vscode-csharp/pull/7941)) From 99561f18654e7429655a75f9e8b923430af40fdf Mon Sep 17 00:00:00 2001 From: David Barbet Date: Fri, 7 Feb 2025 13:53:07 -0800 Subject: [PATCH 20/20] Update CHANGELOG.md Co-authored-by: Joey Robichaud --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5460f0b80..eb698f23a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - Debug from .csproj and .sln [#5876](/~https://github.com/dotnet/vscode-csharp/issues/5876) # 2.65.x -* Update Roslyn to run on .NET 9 (PR: [#7946](/~https://github.com/dotnet/vscode-csharp/pull/7946)) +* Update Roslyn LSP to run on .NET 9 (PR: [#7946](/~https://github.com/dotnet/vscode-csharp/pull/7946)) * Update Roslyn to 4.14.0-2.25106.12 (PR: [#7969](/~https://github.com/dotnet/vscode-csharp/pull/7969)) * Maintain whitespace when converting to switch expression (PR: [#77083](/~https://github.com/dotnet/roslyn/pull/77083)) * Fix extra whitespace insertion for completion text edits (PR: [#77071](/~https://github.com/dotnet/roslyn/pull/77071))