Allow other VS Code extensions to provide custom intellisense configurations. by sherryyshi · Pull Request #1804 · microsoft/vscode-cpptools · GitHub
Skip to content

Allow other VS Code extensions to provide custom intellisense configurations.#1804

Merged
bobbrow merged 41 commits into
microsoft:masterfrom
sherryyshi:user/shersh/prototype
May 18, 2018
Merged

Allow other VS Code extensions to provide custom intellisense configurations.#1804
bobbrow merged 41 commits into
microsoft:masterfrom
sherryyshi:user/shersh/prototype

Conversation

@sherryyshi

Copy link
Copy Markdown
Contributor

Create an API for registering configurations in the currently active language client.

@sherryyshi

sherryyshi commented Apr 9, 2018

Copy link
Copy Markdown
Contributor Author

@WardenGnaw WardenGnaw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution. I will wait on a full review until you mention it is ready.

I have just a few comments that can be addressed.

You can also ignore the
continuous-integration/appveyor/pr failure. That was a leftover artifact from an investigation on testing on Windows.

Comment thread Extension/tsconfig.json Outdated
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"declaration": false,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value of declaration is false. Was there confusion that this needed to be included?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, I was experimenting and checked this in by accident :)

Comment thread Extension/package.json Outdated
@@ -38,7 +38,26 @@
"Linters"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file is rewritten when the extension activates. Please only check in changes you have made to the package.json, and not the extension's rewritten content.

@sherryyshi

sherryyshi commented Apr 10, 2018

Copy link
Copy Markdown
Contributor Author

@WardenGnaw thanks! I think this is ready to be reviewed. If you have questions regarding the background/purpose of this PR, feel free to contact me. #Closed

@WardenGnaw

Copy link
Copy Markdown
Member

@sherryyshi This seems to be mostly configurations from the Language Service. I will have @sean-mcmanus and @bobbrow to take a look at this PR. They may have questions about the purpose of these changes.

Travis-CI is complaining about a couple errors. This it to make sure that the changes follow style guidelines, the project compiles, and it properly activates the extension for debugging. You can find more about the errors in the details link next to
continuous-integration/travis-ci/pr.

@sean-mcmanus

sean-mcmanus commented Apr 10, 2018

Copy link
Copy Markdown
Contributor

Bob said he was going to code review this. #Closed

Comment thread Extension/src/interfaces.ts Outdated
* ------------------------------------------------------------------------------------------ */
'use strict';

export interface ICppTools {

@bobbrow bobbrow Apr 11, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I [](start = 17, length = 1)

nit: we don't use Hungarian naming conventions in TypeScript. Please remove the 'I' from the interface name. #Closed

@bobbrow

bobbrow commented Apr 11, 2018

Copy link
Copy Markdown
Member

@sherryyshi, I sent you an email about this. Let's discuss over email or we can set up another meeting if necessary. #Closed

Comment thread Extension/src/LanguageServer/client.ts Outdated
}

public sendCustomConfiguration(document: vscode.TextDocument, config: SourceFileConfiguration): void {
// TODO: Implement this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would need some help on this.

@bobbrow bobbrow Apr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at the notifications defined at the top of this file under the "Notifications to the server" comment. You'll want to follow that pattern to create the JSON we'll want to send to the language server. It would probably be a vscode.Uri paired with a SourceFileConfiguration.

ChangeFolderSettingsNotification is probably the closest in function to what you will be doing. Then do

this.notifyWhenReady(() => this.languageClient.sendNotification(CustomConfigurationNotification, params));

You should move this method down under the "notifications to the language server" comment below. There are plenty of examples to emulate down there as well. #Closed

Comment thread Extension/cpptools.json Outdated
@@ -1,3 +1,3 @@
{
"intelliSenseEngine_default_percentage": 100
{

@bobbrow bobbrow Apr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert changes to this file (did you remove the last newline?) #Closed

Comment thread Extension/src/api.ts Outdated
defines: string[]; // This must also include the compiler default defines (_MSC_VER, __cplusplus, etc)
intelliSenseMode: string; // Probably just ‘msvc-x64’ for Windows
forcedInclude?: string[]; // Any files that need to be included before the source file is parsed
standard: string; // This is the C or C++ standard (language server will determine if the file is C or C++ based on this)

@bobbrow bobbrow Apr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix indentation (off by one) #Closed

Comment thread Extension/src/api.ts Outdated

export interface SourceFileConfiguration {
includePaths: string[]; // This must also include the system include path (compiler defaults)
defines: string[]; // This must also include the compiler default defines (_MSC_VER, __cplusplus, etc)

@bobbrow bobbrow Apr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove _MSC_VER (Windows-specific) #Closed

Comment thread Extension/src/api.ts Outdated
export interface SourceFileConfiguration {
includePaths: string[]; // This must also include the system include path (compiler defaults)
defines: string[]; // This must also include the compiler default defines (_MSC_VER, __cplusplus, etc)
intelliSenseMode: string; // Probably just ‘msvc-x64’ for Windows

@bobbrow bobbrow Apr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove Windows-specific comment #Closed

Comment thread Extension/src/cppTools.ts Outdated
* ------------------------------------------------------------------------------------------ */
'use strict';

import { CppToolsApi, CustomConfigurationProvider } from "./api";

@bobbrow bobbrow Apr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we always use single quote ' for imports #Closed


// If custom config provider exists, ask for the custom configuration and send a notification to the server
// before the textDocument/didOpen message so that the right information is used when the file is opened.
me.CustomConfigurationProvider.provideConfiguration(document.uri, (config: SourceFileConfiguration) => {

@bobbrow bobbrow Apr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure me.CustomConfigurationProvider is valid first. #Closed

// If custom config provider exists, ask for the custom configuration and send a notification to the server
// before the textDocument/didOpen message so that the right information is used when the file is opened.
me.CustomConfigurationProvider.provideConfiguration(document.uri, (config: SourceFileConfiguration) => {
me.sendCustomConfiguration(document, config); // We have to add this method to client.ts

@bobbrow bobbrow Apr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this comment (you already added the method) #Closed

Comment thread Extension/src/api.ts Outdated

export interface CustomConfigurationProvider {
name: string;
provideConfiguration(uri: vscode.Uri, callback: (config: SourceFileConfiguration) => any): void;

@bobbrow bobbrow Apr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't make this a callback interface. We need the answer immediately, so this method should just return it. #Closed

@sherryyshi sherryyshi Apr 17, 2018

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... Our server needs to do this asynchronously because it's communication with an out-of-proc process vis json rpc... #Closed

@bobbrow bobbrow Apr 17, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making this async may result in vscode messages being sent out of order which will result in undefined behavior and confuse the language server. If you can't synchronize here because you think that your out of proc server is going to take too long to service the request, then we'll need to rethink this. Regardless of what we do here, sendMessage needs to be called before this method returns so you might need to block anyway unless we decide to let the language server open the file with the wrong config until your extension comes back with the correct one.

We probably need some kind of timeout and/or a try/catch block in here to ensure the safety of the provider call (in case a bad extension hangs or crashes) #Closed

// before the textDocument/didOpen message so that the right information is used when the file is opened.
me.CustomConfigurationProvider.provideConfiguration(document.uri, (config: SourceFileConfiguration) => {
me.sendCustomConfiguration(document, config); // We have to add this method to client.ts
sendMessage(document);

@bobbrow bobbrow Apr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to come outside the callback. This message must always be sent. #Closed


reportMacCrashes();

eventEmitter.emit(activationCompleteEventId);

@bobbrow bobbrow Apr 16, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having this event may be unnecessary if we just keep track of the providers in this file and send them down to the Client when they are available. I haven't thought this all the way through yet. I might be able to be more prescriptive once you get the UI part in for selecting providers. #Closed

Comment thread Extension/src/LanguageServer/client.ts Outdated
Name: string = "(empty)";
TrackedDocuments = new Set<vscode.TextDocument>();
CustomConfigurationProvider: CustomConfigurationProvider;
CustomConcigurationProviders: CustomConfigurationProvider[] = [];

@bobbrow bobbrow Apr 17, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo #Closed

Comment thread Extension/src/LanguageServer/client.ts Outdated
uri: document.uri.toString(),
configuration: config
};
this.notifyWhenReady(() => this.languageClient.sendNotification(ChangeCompileCommandsNotification, params));

@bobbrow bobbrow Apr 17, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ChangeCompileCommandsNotification [](start = 72, length = 33)

ChangeCompileCommandsNotification -> CustomConfigurationNotification #Closed

@sherryyshi sherryyshi force-pushed the user/shersh/prototype branch from 2a8775c to 440c304 Compare April 19, 2018 01:15
Comment thread Extension/src/api.ts
import * as vscode from 'vscode';

export interface CppToolsApi {
registerCustomConfigurationProvider(provider: CustomConfigurationProvider): void;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

registerCustomConfigurationProvider [](start = 4, length = 35)

Not blocking for this PR, but can you add comments in here (for each file/property/interface), especially for the API functions? Because this is the public contract for this extension, it should be well-documented

* wait until the all pendingTasks are complete (e.g. language client is ready for use)
* before attempting to send messages
*************************************************************************************/
public runBlockingTask(task: Thenable<any>): Thenable<any> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any [](start = 42, length = 3)

Should this function be generic (i.e. accept type T), so this can be Thenable?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for requestWhenReady below


In reply to: 185363898 [](ancestors = 185363898)

import { SourceFileConfiguration, CustomConfigurationProvider } from '../api';
import { provideCustomConfiguration } from './extension';

function runThenableWithTimeout(promise: Thenable<any>, ms: number): Thenable<any> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runThenableWithTimeout [](start = 9, length = 22)

btw general comment: might be nice to have a utils.ts for the extension with common functions such as this being co-located.

Not blocking for this PR

@saqadri saqadri left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

Comment thread Extension/package.json Outdated
"onCommand:C_Cpp.ResumeParsing",
"onCommand:C_Cpp.ShowParsingCommands",
"onCommand:C_Cpp.TakeSurvey",
"onDebug"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I keep checking this file in by accident... :(

import { SourceFileConfiguration, CustomConfigurationProvider } from '../api';
import { provideCustomConfiguration } from './extension';

function runThenableWithTimeout(promise: Thenable<any>, ms: number): Thenable<any> {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be removed. There is another copy of this in extension.ts which is what is actually being used.


// Returns a race between our timeout and the passed in promise
return client.runBlockingTask(Promise.race([thenable(), timeout]).then((result: any) => {
clearTimeout(timer);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is unnecessary since the timer is cleared in the timeout promise. Will remove in the next push.

Comment thread Extension/src/LanguageServer/client.ts Outdated
const ChangeCompileCommandsNotification: NotificationType<FileChangedParams, void> = new NotificationType<FileChangedParams, void>('cpptools/didChangeCompileCommands');
const ChangeSelectedSettingNotification: NotificationType<FolderSelectedSettingParams, void> = new NotificationType<FolderSelectedSettingParams, void>('cpptools/didChangeSelectedSetting');
const IntervalTimerNotification: NotificationType<void, void> = new NotificationType<void, void>('cpptools/onIntervalTimer');
const CustomConfigurationNotification: NotificationType<SourceFileConfigurationItem[], void> = new NotificationType<SourceFileConfigurationItem[], void>('cpptools/sendCustomConfiguration');

@bobbrow bobbrow May 2, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you keep this as an interface instead of an array of items? It will make our life easier on the language server side. #Closed

}

export function onDidCustomConfigurationChange(customConfigurationProvider: CustomConfigurationProvider): void {
// TODO: How to handle multiple workspaces? Only works with the current workspace.

@bobbrow bobbrow May 2, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

multiple workspaces are handled by ClientCollection. You probably just need to forward this message to ClientCollection and have it iterate on its clients. #Closed

}

let documents: vscode.Uri[] = [];
for (let i: number = 0; i < vscode.workspace.textDocuments.length; ++i) {

@bobbrow bobbrow May 2, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're not going to want to do it this way. This doesn't have any notion of which workspace (read: Client) owns which files. Each Client tracks the files it owns, so you want to have each client request updates for its tracked documents and then send the updated configs to its language server. #Closed


// TODO: Can we rely on clients.ActiveClient to be the client for the current workspace?
runBlockingThenableWithTimeout(() => customConfigurationProvider.provideConfigurations(documents, tokenSource.token), 1000, clients.ActiveClient, tokenSource)
.then((configs: SourceFileConfigurationItem[]) => {

@bobbrow bobbrow May 2, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think you want to .then the runBlockingThenableWithTimeout call rather than the provideConfigurations call so that the language server notification doesn't count toward the timeout. #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... I think that's what it's doing now


In reply to: 185637994 [](ancestors = 185637994)

@bobbrow bobbrow May 3, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! I got confused parsing the long line in my head. Can you reformat?

runBlockingThenableWithTimeout(() => {
    return customConfigurationProvider.provideConfigurations(documents, tokenSource.token);
}, 1000, clients.ActiveClient, tokenSource)
``` #Closed

timer = setTimeout(() => {
clearTimeout(timer);
if (tokenSource) {
tokenSource.cancel();

@bobbrow bobbrow May 2, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you are marking the operation as cancelled, but I don't see any code that actually does anything about it. Are there future changes coming for this? #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The token is passed to our extension, and then passed to our "configuration server", which will react to it. Is there something to be done here as well?

@bobbrow bobbrow May 3, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure how I missed that. I see it now. #Closed

@ThadHouse

Copy link
Copy Markdown

Looking through this, I've noticed that the system headers and system defines need to be included in the provided item. However, the code that grabbed those when passed in a compilerPath seems to be in the low level language service rather then accessable in typescript. Is there any way to get that added to the API? There are some uses for this where the build system doesn't inject the system included directly, but instead just depends on the compiler to have them.

@bobbrow

bobbrow commented May 17, 2018

Copy link
Copy Markdown
Member

@ThadHouse, that's a reasonable request.

Comment thread Extension/src/api.ts Outdated
/**
* This must also include the system include path (compiler defaults)
*/
includePaths: string[];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be renamed includePath to match c_cpp_properties.json

Comment thread Extension/src/api.ts

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ThadHouse @bobbrow does this address the request?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the low level language service cache values for the same compiler path?

This looks great, but I would worry about performance if it checked the compiler for every file, even with the same path.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ThadHouse, yes the language service will cache values to avoid invoking the compiler repeatedly. I am working on this change right now.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more question. The normal docs say skip the headers and the compilerPath is msvc mode is set. Is that still the case with this API?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which docs? Clang for Windows also requires msvc mode, so you should specify the compilerPath in all cases where you want the extension to add system includes and defines for you. We don't specifically query cl.exe in msvc mode yet since the mechanism isn't readily available (we read the registry instead), but we will in the future.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/Microsoft/vscode-cpptools/blob/master/Documentation/LanguageServer/FAQ.md

Specifically this one, where if you want to use MSVC it says don't provide a compilerPath, and only add a compilerPath if using clang for windows.

@bobbrow bobbrow merged commit 01efb1a into microsoft:master May 18, 2018
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 14, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants