Skip to content

Commit

Permalink
Proper Newlines in Hover (#1918)
Browse files Browse the repository at this point in the history
* Added the changes for structured documentation

* Integration Test Running

* Add new test files next to the csproj, not in the root of the workspace

* Make it possible for tests to await restore

* Enable tests to wait for the omnisharp request queue to be empty

* Wait for queue empty in hover test

* Added conditions for each field individually

* Changes for parameter as an object

* Changed to DocumentationItem

* Extracted Documentation helper as a function in a separate file

* Removed unused variable
  • Loading branch information
akshita31 authored Jan 13, 2018
1 parent 04023b5 commit eb66c4b
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 43 deletions.
2 changes: 1 addition & 1 deletion src/features/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ function projectsToCommands(projects: protocol.ProjectDescriptor[]): Promise<Com
});
}

export function dotnetRestoreAllProjects(server: OmniSharpServer) {
export function dotnetRestoreAllProjects(server: OmniSharpServer) : Promise<void> {

if (!server.isRunning()) {
return Promise.reject('OmniSharp server is not running.');
Expand Down
49 changes: 49 additions & 0 deletions src/features/documentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

'use strict';
import * as protocol from '../omnisharp/protocol';

const summaryStartTag = /<summary>/i;
const summaryEndTag = /<\/summary>/i;
Expand All @@ -29,3 +30,51 @@ export function extractSummaryText(xmlDocComment: string): string {

return summary.slice(0, endIndex);
}

export function GetDocumentationString(structDoc: protocol.DocumentationComment) {
let newLine = "\n\n";
let indentSpaces = "\t\t";
let documentation = "";

if (structDoc.SummaryText) {
documentation += structDoc.SummaryText + newLine;
}

if (structDoc.TypeParamElements && structDoc.TypeParamElements.length > 0) {
documentation += "Type Parameters:" + newLine;
documentation += indentSpaces + structDoc.TypeParamElements.map(displayDocumentationObject).join("\n" + indentSpaces) + newLine;
}

if (structDoc.ParamElements && structDoc.ParamElements.length > 0) {
documentation += "Parameters:" + newLine;
documentation += indentSpaces + structDoc.ParamElements.map(displayDocumentationObject).join("\n" + indentSpaces) + newLine;
}

if (structDoc.ReturnsText) {
documentation += structDoc.ReturnsText + newLine;
}

if (structDoc.RemarksText) {
documentation += structDoc.RemarksText + newLine;
}

if (structDoc.ExampleText) {
documentation += structDoc.ExampleText + newLine;
}

if (structDoc.ValueText) {
documentation += structDoc.ValueText + newLine;
}

if (structDoc.Exception && structDoc.Exception.length > 0) {
documentation += "Exceptions:" + newLine;
documentation += indentSpaces + structDoc.Exception.map(displayDocumentationObject).join("\n" + indentSpaces) + newLine;
}

documentation = documentation.trim();
return documentation;
}

export function displayDocumentationObject(obj: protocol.DocumentationItem): string {
return obj.Name + ": " + obj.Documentation;
}
11 changes: 6 additions & 5 deletions src/features/hoverProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@

'use strict';

import {extractSummaryText} from './documentation';
import AbstractSupport from './abstractProvider';
import * as protocol from '../omnisharp/protocol';
import * as serverUtils from '../omnisharp/utils';
import {createRequest} from '../omnisharp/typeConvertion';
import {HoverProvider, Hover, TextDocument, CancellationToken, Position} from 'vscode';
import { createRequest } from '../omnisharp/typeConvertion';
import { HoverProvider, Hover, TextDocument, CancellationToken, Position } from 'vscode';
import { GetDocumentationString } from './documentation';

export default class OmniSharpHoverProvider extends AbstractSupport implements HoverProvider {

Expand All @@ -21,9 +21,10 @@ export default class OmniSharpHoverProvider extends AbstractSupport implements H

return serverUtils.typeLookup(this._server, req, token).then(value => {
if (value && value.Type) {
let contents = [extractSummaryText(value.Documentation), { language: 'csharp', value: value.Type }];
let documentation = GetDocumentationString(value.StructuredDocumentation);
let contents = [documentation, { language: 'csharp', value: value.Type }];
return new Hover(contents);
}
});
}
}
}
3 changes: 3 additions & 0 deletions src/omnisharp/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import forwardChanges from '../features/changeForwarding';
import registerCommands from '../features/commands';
import reportStatus from '../features/status';

export let omnisharp: OmniSharpServer;

export function activate(context: vscode.ExtensionContext, reporter: TelemetryReporter, channel: vscode.OutputChannel) {
const documentSelector: vscode.DocumentSelector = {
language: 'csharp',
Expand All @@ -42,6 +44,7 @@ export function activate(context: vscode.ExtensionContext, reporter: TelemetryRe
const options = Options.Read();

const server = new OmniSharpServer(reporter);
omnisharp = server;
const advisor = new Advisor(server); // create before server is started
const disposables: vscode.Disposable[] = [];
const localDisposables: vscode.Disposable[] = [];
Expand Down
24 changes: 20 additions & 4 deletions src/omnisharp/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,30 @@ export interface FindSymbolsResponse {
QuickFixes: SymbolLocation[];
}

export interface DocumentationItem {
Name: string;
Documentation: string;
}

export interface DocumentationComment {
SummaryText: string;
TypeParamElements: DocumentationItem[];
ParamElements: DocumentationItem[];
ReturnsText: string;
RemarksText: string;
ExampleText: string;
ValueText: string;
Exception: DocumentationItem[];
}

export interface TypeLookupRequest extends Request {
IncludeDocumentation: boolean;
}

export interface TypeLookupResponse {
Type: string;
Documentation: string;
StructuredDocumentation: DocumentationComment;
}

export interface RunCodeActionResponse {
Expand Down Expand Up @@ -418,13 +435,12 @@ export interface PackageDependency {
Name: string;
Version: string;
}
export interface FilesChangedRequest extends Request{

export interface FilesChangedRequest extends Request {
ChangeType: FileChangeType;
}

export enum FileChangeType
{
export enum FileChangeType {
Change = "Change",
Create = "Create",
Delete = "Delete"
Expand Down
7 changes: 7 additions & 0 deletions src/omnisharp/requestQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@ export class RequestQueueCollection {
}
}

public isEmpty()
{
return !this._deferredQueue.hasPending()
&& !this._normalQueue.hasPending()
&& !this._priorityQueue.hasPending();
}

public enqueue(request: Request) {
const queue = this.getQueue(request.command);
queue.enqueue(request);
Expand Down
8 changes: 8 additions & 0 deletions src/omnisharp/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import * as path from 'path';
import * as protocol from './protocol';
import * as utils from '../common';
import * as vscode from 'vscode';
import { setTimeout } from 'timers';

enum ServerState {
Starting,
Expand Down Expand Up @@ -98,6 +99,13 @@ export class OmniSharpServer {
return this._state === ServerState.Started;
}

public async waitForEmptyEventQueue() : Promise<void> {
while (!this._requestQueue.isEmpty()) {
let p = new Promise((resolve) => setTimeout(resolve, 100));
await p;
}
}

private _getState(): ServerState {
return this._state;
}
Expand Down
83 changes: 83 additions & 0 deletions test/integrationTests/hoverProvider.integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as fs from 'async-file';
import * as vscode from 'vscode';

import poll from './poll';
import { should, expect } from 'chai';
import testAssetWorkspace from './testAssets/testAssetWorkspace';
import { RequestQueueCollection } from '../../src/omnisharp/requestQueue';
import { OmniSharpServer } from '../../src/omnisharp/server';
import { omnisharp } from '../../src/omnisharp/extension';

const chai = require('chai');
chai.use(require('chai-arrays'));
chai.use(require('chai-fs'));

suite(`Tasks generation: ${testAssetWorkspace.description}`, function() {
suiteSetup(async function() {
should();

let csharpExtension = vscode.extensions.getExtension("ms-vscode.csharp");
if (!csharpExtension.isActive) {
await csharpExtension.activate();
}

await testAssetWorkspace.cleanupWorkspace();

await csharpExtension.exports.initializationFinished;

await vscode.commands.executeCommand("dotnet.generateAssets");

await poll(async () => await fs.exists(testAssetWorkspace.launchJsonPath), 10000, 100);

});


test("Hover returns structured documentation with proper newlines", async function () {

let program =
`using System;
namespace Test
{
class testissue
{
///<summary>Checks if object is tagged with the tag.</summary>
/// <param name="gameObject">The game object.</param>
/// <param name="tagName">Name of the tag.</param>
/// <returns>Returns <c> true</c> if object is tagged with tag.</returns>
public static bool Compare(int gameObject,string tagName)
{
return true;
}
}
}`;
let fileUri = await testAssetWorkspace.projects[0].addFileWithContents("test1.cs", program);

await omnisharp.waitForEmptyEventQueue();

await vscode.commands.executeCommand("vscode.open", fileUri);

let c = await vscode.commands.executeCommand("vscode.executeHoverProvider", fileUri,new vscode.Position(10,29));

let answer:string =
`Checks if object is tagged with the tag.
Parameters:
\t\tgameObject: The game object.
\t\ttagName: Name of the tag.
Returns trueif object is tagged with tag.`;
expect(c[0].contents[0].value).to.equal(answer);
});

teardown(async() =>
{
await testAssetWorkspace.cleanupWorkspace();
})
});
36 changes: 18 additions & 18 deletions test/integrationTests/launchConfiguration.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,42 @@ import poll from './poll';
import { should } from 'chai';
import testAssetWorkspace from './testAssets/testAssetWorkspace';

const chai = require('chai');
chai.use(require('chai-arrays'));
chai.use(require('chai-fs'));
suite(`Tasks generation: ${testAssetWorkspace.description}`, function() {
suiteSetup(async function() {
const chai = require('chai');
chai.use(require('chai-arrays'));
chai.use(require('chai-fs'));

suite(`Tasks generation: ${testAssetWorkspace.description}`, function () {
suiteSetup(async function () {
should();

let csharpExtension = vscode.extensions.getExtension("ms-vscode.csharp");
if (!csharpExtension.isActive) {
await csharpExtension.activate();
let csharpExtension = vscode.extensions.getExtension("ms-vscode.csharp");
if (!csharpExtension.isActive) {
await csharpExtension.activate();
}

await testAssetWorkspace.cleanupWorkspace();

await csharpExtension.exports.initializationFinished;

await vscode.commands.executeCommand("dotnet.generateAssets");

await poll(async () => await fs.exists(testAssetWorkspace.launchJsonPath), 10000, 100);
});

test("Starting .NET Core Launch (console) from the workspace root should create an Active Debug Session", async () => {

});

test("Starting .NET Core Launch (console) from the workspace root should create an Active Debug Session", async () => {
await vscode.debug.startDebugging(vscode.workspace.workspaceFolders[0], ".NET Core Launch (console)");

let debugSessionTerminated = new Promise(resolve => {
vscode.debug.onDidTerminateDebugSession((e) => resolve());
});

vscode.debug.activeDebugSession.type.should.equal("coreclr");

await debugSessionTerminated;
});

teardown(async() =>
{
teardown(async () => {
await testAssetWorkspace.cleanupWorkspace();
})
});
});
});
Loading

0 comments on commit eb66c4b

Please sign in to comment.