Skip to content

Commit 09d8fd9

Browse files
authored
Merge pull request #7321 from dotnet/dev/jorobich/project-context-item
Add Project Context status item
2 parents 2bacb5c + a7d58a2 commit 09d8fd9

File tree

6 files changed

+136
-2
lines changed

6 files changed

+136
-2
lines changed

l10n/bundle.l10n.json

+2
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@
161161
"Fix All: ": "Fix All: ",
162162
"C# Workspace Status": "C# Workspace Status",
163163
"Open solution": "Open solution",
164+
"C# Project Context Status": "C# Project Context Status",
165+
"Active File Context": "Active File Context",
164166
"Pick a fix all scope": "Pick a fix all scope",
165167
"Fix All Code Action": "Fix All Code Action",
166168
"pipeArgs must be a string or a string array type": "pipeArgs must be a string or a string array type",

src/lsptoolshost/languageStatusBar.ts

+20
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export function registerLanguageStatusItems(
1919
if (!getCSharpDevKit()) {
2020
WorkspaceStatus.createStatusItem(context, languageServerEvents);
2121
}
22+
ProjectContextStatus.createStatusItem(context, languageServer);
2223
}
2324

2425
class WorkspaceStatus {
@@ -40,3 +41,22 @@ class WorkspaceStatus {
4041
});
4142
}
4243
}
44+
45+
class ProjectContextStatus {
46+
static createStatusItem(context: vscode.ExtensionContext, languageServer: RoslynLanguageServer) {
47+
const projectContextService = languageServer._projectContextService;
48+
49+
const item = vscode.languages.createLanguageStatusItem(
50+
'csharp.projectContextStatus',
51+
languageServerOptions.documentSelector
52+
);
53+
item.name = vscode.l10n.t('C# Project Context Status');
54+
item.detail = vscode.l10n.t('Active File Context');
55+
context.subscriptions.push(item);
56+
57+
projectContextService.onActiveFileContextChanged((e) => {
58+
item.text = e.context._vs_label;
59+
});
60+
projectContextService.refresh();
61+
}
62+
}

src/lsptoolshost/roslynLanguageServer.ts

+4
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import { BuildDiagnosticsService } from './buildDiagnosticsService';
6565
import { getComponentPaths } from './builtInComponents';
6666
import { OnAutoInsertFeature } from './onAutoInsertFeature';
6767
import { registerLanguageStatusItems } from './languageStatusBar';
68+
import { ProjectContextService } from './services/projectContextService';
6869

6970
let _channel: vscode.OutputChannel;
7071
let _traceChannel: vscode.OutputChannel;
@@ -106,6 +107,7 @@ export class RoslynLanguageServer {
106107
public readonly _onAutoInsertFeature: OnAutoInsertFeature;
107108

108109
public _buildDiagnosticService: BuildDiagnosticsService;
110+
public _projectContextService: ProjectContextService;
109111

110112
constructor(
111113
private _languageClient: RoslynLanguageClient,
@@ -125,6 +127,8 @@ export class RoslynLanguageServer {
125127
this._buildDiagnosticService = new BuildDiagnosticsService(diagnosticsReportedByBuild);
126128
this.registerDocumentOpenForDiagnostics();
127129

130+
this._projectContextService = new ProjectContextService(this, this._languageServerEvents);
131+
128132
// Register Razor dynamic file info handling
129133
this.registerDynamicFileInfo();
130134

src/lsptoolshost/roslynProtocol.ts

+28
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,21 @@ import * as lsp from 'vscode-languageserver-protocol';
88
import { CodeAction, TextDocumentRegistrationOptions } from 'vscode-languageserver-protocol';
99
import { ProjectConfigurationMessage } from '../shared/projectConfiguration';
1010

11+
export interface VSProjectContextList {
12+
_vs_projectContexts: VSProjectContext[];
13+
_vs_defaultIndex: number;
14+
}
15+
16+
export interface VSProjectContext {
17+
_vs_label: string;
18+
_vs_id: string;
19+
_vs_kind: string;
20+
}
21+
22+
export interface VSTextDocumentIdentifier extends lsp.TextDocumentIdentifier {
23+
_vs_projectContext: VSProjectContext | undefined;
24+
}
25+
1126
export interface WorkspaceDebugConfigurationParams {
1227
/**
1328
* Workspace path containing the solution/projects to get debug information for.
@@ -88,6 +103,13 @@ export interface RegisterSolutionSnapshotResponseItem {
88103
id: lsp.integer;
89104
}
90105

106+
export interface VSGetProjectContextParams {
107+
/**
108+
* The document the project context is being requested for.
109+
*/
110+
_vs_textDocument: lsp.TextDocumentIdentifier;
111+
}
112+
91113
export interface RunTestsParams extends lsp.WorkDoneProgressParams, lsp.PartialResultParams {
92114
/**
93115
* The text document containing the tests to run.
@@ -210,6 +232,12 @@ export namespace RegisterSolutionSnapshotRequest {
210232
export const type = new lsp.RequestType0<RegisterSolutionSnapshotResponseItem, void>(method);
211233
}
212234

235+
export namespace VSGetProjectContextsRequest {
236+
export const method = 'textDocument/_vs_getProjectContexts';
237+
export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.clientToServer;
238+
export const type = new lsp.RequestType<VSGetProjectContextParams, VSProjectContextList, void>(method);
239+
}
240+
213241
export namespace ProjectInitializationCompleteNotification {
214242
export const method = 'workspace/projectInitializationComplete';
215243
export const messageDirection: lsp.MessageDirection = lsp.MessageDirection.serverToClient;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
import { RoslynLanguageServer } from '../roslynLanguageServer';
8+
import { VSGetProjectContextsRequest, VSProjectContext, VSProjectContextList } from '../roslynProtocol';
9+
import { TextDocumentIdentifier } from 'vscode-languageserver-protocol';
10+
import { UriConverter } from '../uriConverter';
11+
import { LanguageServerEvents } from '../languageServerEvents';
12+
import { ServerState } from '../serverStateChange';
13+
14+
export interface ProjectContextChangeEvent {
15+
uri: vscode.Uri;
16+
context: VSProjectContext;
17+
}
18+
19+
export class ProjectContextService {
20+
private readonly _contextChangeEmitter = new vscode.EventEmitter<ProjectContextChangeEvent>();
21+
private _source = new vscode.CancellationTokenSource();
22+
23+
constructor(private _languageServer: RoslynLanguageServer, _languageServerEvents: LanguageServerEvents) {
24+
_languageServerEvents.onServerStateChange((e) => {
25+
// When the project initialization is complete, open files
26+
// could move from the miscellaneous workspace context into
27+
// an open project.
28+
if (e.state === ServerState.ProjectInitializationComplete) {
29+
this.refresh();
30+
}
31+
});
32+
33+
vscode.window.onDidChangeActiveTextEditor(async (_) => this.refresh());
34+
}
35+
36+
public get onActiveFileContextChanged(): vscode.Event<ProjectContextChangeEvent> {
37+
return this._contextChangeEmitter.event;
38+
}
39+
40+
public async refresh() {
41+
const textEditor = vscode.window.activeTextEditor;
42+
if (textEditor?.document?.languageId !== 'csharp') {
43+
return;
44+
}
45+
46+
const uri = textEditor.document.uri;
47+
48+
// If we have an open request, cancel it.
49+
this._source.cancel();
50+
this._source = new vscode.CancellationTokenSource();
51+
52+
const contextList = await this.getProjectContexts(uri, this._source.token);
53+
if (!contextList) {
54+
return;
55+
}
56+
57+
const context = contextList._vs_projectContexts[contextList._vs_defaultIndex];
58+
this._contextChangeEmitter.fire({ uri, context });
59+
}
60+
61+
private async getProjectContexts(
62+
uri: vscode.Uri,
63+
token: vscode.CancellationToken
64+
): Promise<VSProjectContextList | undefined> {
65+
const uriString = UriConverter.serialize(uri);
66+
const textDocument = TextDocumentIdentifier.create(uriString);
67+
68+
try {
69+
return this._languageServer.sendRequest(
70+
VSGetProjectContextsRequest.type,
71+
{ _vs_textDocument: textDocument },
72+
token
73+
);
74+
} catch (error) {
75+
if (error instanceof vscode.CancellationError) {
76+
return undefined;
77+
}
78+
79+
throw error;
80+
}
81+
}
82+
}

test/integrationTests/documentDiagnostics.integration.test.ts

-2
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,6 @@ describe(`[${testAssetWorkspace.description}] Test diagnostics`, function () {
8585
analyzer: AnalysisSetting.OpenFiles,
8686
});
8787

88-
await integrationHelpers.restartLanguageServer();
89-
9088
await waitForExpectedFileDiagnostics((diagnostics) => {
9189
expect(diagnostics).toHaveLength(4);
9290

0 commit comments

Comments
 (0)