Skip to content

Commit f807baf

Browse files
authored
Merge pull request #5400 from 50Wliu/users/winstonliu/feature-nullability
Feature nullability
2 parents ef7d6cc + 301ca1d commit f807baf

33 files changed

+536
-558
lines changed

package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,17 +1189,20 @@
11891189
{
11901190
"command": "o.fixAll.solution",
11911191
"title": "Fix all occurrences of a code issue within solution",
1192-
"category": "OmniSharp"
1192+
"category": "OmniSharp",
1193+
"enablement": "editorFocus"
11931194
},
11941195
{
11951196
"command": "o.fixAll.project",
11961197
"title": "Fix all occurrences of a code issue within project",
1197-
"category": "OmniSharp"
1198+
"category": "OmniSharp",
1199+
"enablement": "editorFocus"
11981200
},
11991201
{
12001202
"command": "o.fixAll.document",
12011203
"title": "Fix all occurrences of a code issue within document",
1202-
"category": "OmniSharp"
1204+
"category": "OmniSharp",
1205+
"enablement": "editorFocus"
12031206
},
12041207
{
12051208
"command": "o.reanalyze.allProjects",
@@ -1209,7 +1212,8 @@
12091212
{
12101213
"command": "o.reanalyze.currentProject",
12111214
"title": "Analyze current project",
1212-
"category": "OmniSharp"
1215+
"category": "OmniSharp",
1216+
"enablement": "editorFocus"
12131217
},
12141218
{
12151219
"command": "dotnet.generateAssets",

src/features/codeActionProvider.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default class CodeActionProvider extends AbstractProvider implements vsco
2323
this.addDisposables(new CompositeDisposable(registerCommandDisposable));
2424
}
2525

26-
public async provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken): Promise<vscode.CodeAction[]> {
26+
public async provideCodeActions(document: vscode.TextDocument, range: vscode.Range | vscode.Selection, context: vscode.CodeActionContext, token: vscode.CancellationToken): Promise<vscode.CodeAction[] | undefined> {
2727
const options = this.optionProvider.GetLatestOptions();
2828
if (options.disableCodeActions) {
2929
return;
@@ -75,7 +75,7 @@ export default class CodeActionProvider extends AbstractProvider implements vsco
7575
}
7676
}
7777

78-
private mapOmniSharpCodeActionKindToVSCodeCodeActionKind(kind: string): vscode.CodeActionKind {
78+
private mapOmniSharpCodeActionKindToVSCodeCodeActionKind(kind: string | undefined): vscode.CodeActionKind {
7979
switch (kind) {
8080
case 'QuickFix':
8181
return vscode.CodeActionKind.QuickFix;
@@ -90,7 +90,7 @@ export default class CodeActionProvider extends AbstractProvider implements vsco
9090
}
9191
}
9292

93-
private async _runCodeAction(req: protocol.V2.RunCodeActionRequest, token: vscode.CancellationToken): Promise<boolean | string | {}> {
93+
private async _runCodeAction(req: protocol.V2.RunCodeActionRequest, token: vscode.CancellationToken): Promise<boolean | string | undefined> {
9494
try {
9595
const response = await serverUtils.runCodeAction(this._server, req);
9696
if (response) {
@@ -99,5 +99,7 @@ export default class CodeActionProvider extends AbstractProvider implements vsco
9999
} catch (error) {
100100
return Promise.reject(`Problem invoking 'RunCodeAction' on OmniSharp server: ${error}`);
101101
}
102+
103+
return undefined;
102104
}
103105
}

src/features/codeLensProvider.ts

Lines changed: 44 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ class DebugTestsCodeLens extends TestCodeLens {
7777
}
7878
}
7979

80+
interface Test {
81+
framework: string;
82+
methodName: string;
83+
}
84+
8085
export default class OmniSharpCodeLensProvider extends AbstractProvider implements vscode.CodeLensProvider {
8186

8287
constructor(server: OmniSharpServer, testManager: TestManager, private optionProvider: OptionProvider, languageMiddlewareFeature: LanguageMiddlewareFeature) {
@@ -100,7 +105,7 @@ export default class OmniSharpCodeLensProvider extends AbstractProvider implemen
100105
return [];
101106
}
102107

103-
async resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens> {
108+
async resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens | undefined> {
104109
if (codeLens instanceof ReferencesCodeLens) {
105110
return this.resolveReferencesCodeLens(codeLens, token);
106111
}
@@ -110,9 +115,11 @@ export default class OmniSharpCodeLensProvider extends AbstractProvider implemen
110115
else if (codeLens instanceof DebugTestsCodeLens) {
111116
return this.resolveTestCodeLens(codeLens, 'Debug Test', 'dotnet.test.debug', 'Debug All Tests', 'dotnet.classTests.debug');
112117
}
118+
119+
return undefined;
113120
}
114121

115-
private async resolveReferencesCodeLens(codeLens: ReferencesCodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens> {
122+
private async resolveReferencesCodeLens(codeLens: ReferencesCodeLens, token: vscode.CancellationToken): Promise<vscode.CodeLens | undefined> {
116123
const request: protocol.FindUsagesRequest = {
117124
FileName: codeLens.fileName,
118125
Line: codeLens.range.start.line,
@@ -137,7 +144,7 @@ export default class OmniSharpCodeLensProvider extends AbstractProvider implemen
137144
codeLens.command = {
138145
title: count === 1 ? '1 reference' : `${count} references`,
139146
command: 'editor.action.showReferences',
140-
arguments: [vscode.Uri.file(request.FileName), codeLens.range.start, remappedLocations]
147+
arguments: [vscode.Uri.file(codeLens.fileName), codeLens.range.start, remappedLocations]
141148
};
142149

143150
return codeLens;
@@ -147,7 +154,7 @@ export default class OmniSharpCodeLensProvider extends AbstractProvider implemen
147154
}
148155
}
149156

150-
private async resolveTestCodeLens(codeLens: TestCodeLens, singularTitle: string, singularCommandName: string, pluralTitle: string, pluralCommandName: string): Promise<vscode.CodeLens> {
157+
private async resolveTestCodeLens(codeLens: TestCodeLens, singularTitle: string, singularCommandName: string, pluralTitle: string, pluralCommandName: string): Promise<vscode.CodeLens | undefined> {
151158
if (!codeLens.isTestContainer) {
152159
// This is just a single test method, not a container.
153160
codeLens.command = {
@@ -202,35 +209,33 @@ function createCodeLensesForElement(element: Structure.CodeElement, fileName: st
202209
}
203210

204211
if (options.showTestsCodeLens) {
205-
if (isValidMethodForTestCodeLens(element)) {
206-
let [testFramework, testMethodName] = getTestFrameworkAndMethodName(element);
207-
let range = element.Ranges[SymbolRangeNames.Name];
208-
209-
if (range && testFramework && testMethodName) {
210-
results.push(new RunTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ false, testFramework, [testMethodName]));
211-
results.push(new DebugTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ false, testFramework, [testMethodName]));
212+
if (element.Kind === SymbolKinds.Method) {
213+
const test = getTest(element);
214+
if (test !== undefined) {
215+
let range = element.Ranges[SymbolRangeNames.Name];
216+
if (range !== undefined) {
217+
results.push(new RunTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ false, test.framework, [test.methodName]));
218+
results.push(new DebugTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ false, test.framework, [test.methodName]));
219+
}
212220
}
213-
}
214-
else if (isValidClassForTestCodeLens(element)) {
215-
// Note: We don't handle multiple test frameworks in the same class. The first test framework wins.
216-
let testFramework: string = null;
217-
let testMethodNames: string[] = [];
218-
let range = element.Ranges[SymbolRangeNames.Name];
221+
} else if (element.Kind === SymbolKinds.Class && element.Children !== undefined) {
222+
const methods = element.Children.filter(child => child.Kind === SymbolKinds.Method);
219223

220-
for (let childElement of element.Children) {
221-
let [childTestFramework, childTestMethodName] = getTestFrameworkAndMethodName(childElement);
224+
const tests = methods
225+
.map(method => getTest(method))
226+
.filter((test): test is NonNullable<typeof test> => test !== undefined);
222227

223-
if (!testFramework && childTestFramework) {
224-
testFramework = childTestFramework;
225-
testMethodNames.push(childTestMethodName);
226-
}
227-
else if (testFramework && childTestFramework === testFramework) {
228-
testMethodNames.push(childTestMethodName);
229-
}
228+
// Note: We don't handle multiple test frameworks in the same class. The first test framework wins.
229+
const testFramework = tests.length > 0 ? tests[0].framework : undefined;
230+
if (testFramework !== undefined) {
231+
const testMethodNames = tests
232+
.filter(test => test.framework === testFramework)
233+
.map(test => test.methodName);
234+
235+
let range = element.Ranges[SymbolRangeNames.Name];
236+
results.push(new RunTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ true, testFramework, testMethodNames));
237+
results.push(new DebugTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ true, testFramework, testMethodNames));
230238
}
231-
232-
results.push(new RunTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ true, testFramework, testMethodNames));
233-
results.push(new DebugTestsCodeLens(range, fileName, element.DisplayName,/*isTestContainer*/ true, testFramework, testMethodNames));
234239
}
235240
}
236241

@@ -262,40 +267,19 @@ function isValidElementForReferencesCodeLens(element: Structure.CodeElement, opt
262267
return true;
263268
}
264269

265-
266-
function isValidClassForTestCodeLens(element: Structure.CodeElement): boolean {
267-
if (element.Kind != SymbolKinds.Class) {
268-
return false;
270+
function getTest(element: Structure.CodeElement): Test | undefined {
271+
if (element.Properties === undefined) {
272+
return undefined;
269273
}
270274

271-
if (!element.Children) {
272-
return false;
275+
const framework = element.Properties[SymbolPropertyNames.TestFramework];
276+
const methodName = element.Properties[SymbolPropertyNames.TestMethodName];
277+
if (framework === undefined || methodName === undefined) {
278+
return undefined;
273279
}
274280

275-
return element.Children.find(isValidMethodForTestCodeLens) !== undefined;
276-
}
277-
278-
function isValidMethodForTestCodeLens(element: Structure.CodeElement): boolean {
279-
if (element.Kind != SymbolKinds.Method) {
280-
return false;
281+
return {
282+
framework,
283+
methodName,
281284
}
282-
283-
if (!element.Properties ||
284-
!element.Properties[SymbolPropertyNames.TestFramework] ||
285-
!element.Properties[SymbolPropertyNames.TestMethodName]) {
286-
return false;
287-
}
288-
289-
return true;
290-
}
291-
292-
function getTestFrameworkAndMethodName(element: Structure.CodeElement): [string, string] {
293-
if (!element.Properties) {
294-
return [null, null];
295-
}
296-
297-
const testFramework = element.Properties[SymbolPropertyNames.TestFramework];
298-
const testMethodName = element.Properties[SymbolPropertyNames.TestMethodName];
299-
300-
return [testFramework, testMethodName];
301285
}

src/features/commands.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export default function registerCommands(context: vscode.ExtensionContext, serve
5353
// Register command for generating tasks.json and launch.json assets.
5454
disposable.add(vscode.commands.registerCommand('dotnet.generateAssets', async (selectedIndex) => generateAssets(server, selectedIndex)));
5555

56-
disposable.add(vscode.commands.registerCommand('csharp.reportIssue', async () => reportIssue(vscode, eventStream, getDotnetInfo, platformInfo.isValidPlatformForMono(), optionProvider.GetLatestOptions(), monoResolver, dotnetResolver)));
56+
disposable.add(vscode.commands.registerCommand('csharp.reportIssue', async () => reportIssue(vscode, context.extension.packageJSON.version, eventStream, getDotnetInfo, platformInfo.isValidPlatformForMono(), optionProvider.GetLatestOptions(), dotnetResolver, monoResolver)));
5757

5858
disposable.add(vscode.commands.registerCommand('csharp.showDecompilationTerms', async () => showDecompilationTerms(context, server, optionProvider)));
5959

@@ -154,8 +154,13 @@ async function reAnalyzeAllProjects(server: OmniSharpServer, eventStream: EventS
154154
}
155155

156156
async function reAnalyzeCurrentProject(server: OmniSharpServer, eventStream: EventStream): Promise<void> {
157+
const activeTextEditor = vscode.window.activeTextEditor;
158+
if (activeTextEditor === undefined) {
159+
return;
160+
}
161+
157162
await serverUtils.reAnalyze(server, {
158-
fileName: vscode.window.activeTextEditor.document.uri.fsPath
163+
FileName: activeTextEditor.document.uri.fsPath,
159164
});
160165
}
161166

src/features/completionProvider.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ export default class OmnisharpCompletionProvider extends AbstractProvider implem
2323
super(server, languageMiddlewareFeature);
2424
}
2525

26-
public async provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): Promise<CompletionList> {
26+
public async provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): Promise<CompletionList | undefined> {
2727
let request = createRequest<protocol.CompletionRequest>(document, position);
2828
request.CompletionTrigger = (context.triggerKind + 1) as LspCompletionTriggerKind;
2929
request.TriggerCharacter = context.triggerCharacter;
3030

3131
try {
3232
const response = await serverUtils.getCompletion(this._server, request, token);
33-
let mappedItems = response.Items.map(arg => this._convertToVscodeCompletionItem(arg));
33+
let mappedItems = response.Items.map(arg => this._convertToVscodeCompletionItem(arg, document));
3434

3535
if (isVirtualCSharpDocument(document)) {
3636
// The `await` completion item is not compatible with all Razor scenarios.
@@ -89,16 +89,16 @@ export default class OmnisharpCompletionProvider extends AbstractProvider implem
8989
const request: protocol.CompletionResolveRequest = { Item: lspItem };
9090
try {
9191
const response = await serverUtils.getCompletionResolve(this._server, request, token);
92-
return this._convertToVscodeCompletionItem(response.Item);
92+
return this._convertToVscodeCompletionItem(response.Item, item.command?.arguments?.[1] as TextDocument);
9393
}
9494
catch (error) {
95-
return;
95+
return item;
9696
}
9797
}
9898

99-
public async afterInsert(item: protocol.OmnisharpCompletionItem) {
99+
public async afterInsert(item: protocol.OmnisharpCompletionItem, document: TextDocument) {
100100
try {
101-
const uri = window.activeTextEditor.document.uri;
101+
const uri = document.uri;
102102
const response = await serverUtils.getCompletionAfterInsert(this._server, { Item: item });
103103

104104
if (!response.Changes || !response.Column || !response.Line) {
@@ -119,18 +119,20 @@ export default class OmnisharpCompletionProvider extends AbstractProvider implem
119119
return;
120120
}
121121

122-
const responseLine = response.Line;
123-
const responseColumn = response.Column;
122+
const editor = window.visibleTextEditors.find(editor => editor.document === document);
123+
if (editor === undefined) {
124+
return;
125+
}
124126

125-
const finalPosition = new Position(responseLine, responseColumn);
126-
window.activeTextEditor.selections = [new Selection(finalPosition, finalPosition)];
127+
const finalPosition = new Position(response.Line, response.Column);
128+
editor.selections = [new Selection(finalPosition, finalPosition)];
127129
}
128130
catch (error) {
129131
return;
130132
}
131133
}
132134

133-
private _convertToVscodeCompletionItem(omnisharpCompletion: protocol.OmnisharpCompletionItem): CompletionItem {
135+
private _convertToVscodeCompletionItem(omnisharpCompletion: protocol.OmnisharpCompletionItem, document: TextDocument): CompletionItem {
134136
const docs: MarkdownString | undefined = omnisharpCompletion.Documentation ? new MarkdownString(omnisharpCompletion.Documentation, false) : undefined;
135137

136138
const mapRange = function (edit: protocol.LinePositionSpanTextChange): Range {
@@ -165,7 +167,7 @@ export default class OmnisharpCompletionProvider extends AbstractProvider implem
165167
sortText: omnisharpCompletion.SortText,
166168
additionalTextEdits: additionalTextEdits,
167169
keepWhitespace: true,
168-
command: omnisharpCompletion.HasAfterInsertStep ? { command: CompletionAfterInsertCommand, title: "", arguments: [omnisharpCompletion] } : undefined
170+
command: omnisharpCompletion.HasAfterInsertStep ? { command: CompletionAfterInsertCommand, title: "", arguments: [omnisharpCompletion, document] } : undefined
169171
};
170172
}
171173
}

src/features/definitionMetadataDocumentProvider.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { IDisposable } from '../Disposable';
99

1010
export default class DefinitionMetadataDocumentProvider implements TextDocumentContentProvider, IDisposable {
1111
readonly scheme = "omnisharp-metadata";
12-
private _registration: IDisposable;
12+
private _registration?: IDisposable;
1313
private _documents: Map<string, MetadataResponse>;
1414
private _documentClosedSubscription: IDisposable;
1515

@@ -23,7 +23,7 @@ export default class DefinitionMetadataDocumentProvider implements TextDocumentC
2323
}
2424

2525
public dispose(): void {
26-
this._registration.dispose();
26+
this._registration?.dispose();
2727
this._documentClosedSubscription.dispose();
2828
this._documents.clear();
2929
}
@@ -48,12 +48,12 @@ export default class DefinitionMetadataDocumentProvider implements TextDocumentC
4848
this._registration = workspace.registerTextDocumentContentProvider(this.scheme, this);
4949
}
5050

51-
public provideTextDocumentContent(uri: Uri): string {
51+
public provideTextDocumentContent(uri: Uri): string | undefined {
5252
return this._documents.get(uri.toString())?.Source;
5353
}
5454

5555
private createUri(sourceName: string): Uri {
5656
return Uri.parse(this.scheme + "://" +
5757
sourceName.replace(/\\/g, "/").replace(/(.*)\/(.*)/g, "$1/[metadata] $2"));
5858
}
59-
}
59+
}

0 commit comments

Comments
 (0)