Skip to content

Conversation

d3r3kk
Copy link

@d3r3kk d3r3kk commented Jul 17, 2018

Note: Working on unit tests now. Starting the PR to get some advance feedback.

Fixes #2127

  • Title summarizes what is changing
  • Includes a news entry file (remember to thank yourself!)
  • Unit tests & code coverage are not adversely affected (within reason)
  • Works on all actively maintained versions of Python (e.g. Python 2.7 & the latest Python 3 release)
  • Works on Windows 10, macOS, and Linux (e.g. considered file system case-sensitivity)
  • N/A Dependencies are pinned (e.g. "1.2.3", not "^1.2.3")
  • N/A package-lock.json has been regenerated if dependencies have changed

PR Feedback:

TODO:

  • Remove remnant of onNotification.bind.
  • Use interfaces for new popups instead of the actual class references.
  • Switch back to DI instead of manual construction... (hence the interface switch above).
  • Rename the class with the New prefix to lose the New.
  • Rename ProposeNewLanguageServerBanner to ProposeLanguageServerBanner.
  • remove export from utils.getRandom.
  • Remove trigger count as we can test that scenario without the use of a contrived counter.

@d3r3kk d3r3kk requested a review from DonJayamanne July 17, 2018 19:53
@d3r3kk
Copy link
Author

d3r3kk commented Jul 17, 2018

@DonJayamanne & @brettcannon apologies for taking so long! Learning while doing...

}
));

this.surveyBanner = new NewLanguageServerSurveyBanner(

Choose a reason for hiding this comment

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

Why aren't we resolving the survey banner instead of manually constructing it. I.e. use DI instead of manual construction.

async (args) => {
if (this.languageClient) {
await this.startupCompleted.promise;
this.languageClient.onNotification.bind(this);

Choose a reason for hiding this comment

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

Why is this necessary?

Copy link
Author

Choose a reason for hiding this comment

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

Thanks, I was trying to figure out how to capture the 'onNotification' event from the BaseLanguageClient worked at the time, this is just a remnant. Thanks for catching this!

private excludedFiles: string[] = [];
private typeshedPaths: string[] = [];
private loadExtensionArgs: {} | undefined;
private surveyBanner: NewLanguageServerSurveyBanner;

Choose a reason for hiding this comment

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

Please use an interface, not a class.

Copy link
Author

Choose a reason for hiding this comment

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

Can do, I think I've learned enough from doing these two popups to figure out what I can make more generic.

import { IBrowserService, IConfigurationService, IExtensionContext,
IOutputChannel, IPersistentStateFactory, IPythonSettings } from '../common/types';
import { IServiceContainer } from '../ioc/types';
import { NewLanguageServerSurveyBanner } from '../languageServices/newLanguageServerSurveyBanner';

Choose a reason for hiding this comment

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

Please drop the New prefix.

Copy link
Author

Choose a reason for hiding this comment

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

👍

function enables the popup for this user.
*/

export class ProposeNewLanguageServerBanner {

Choose a reason for hiding this comment

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

Please change to LanguageServerBanner.
We've used the prefix Experimental in the ExperimentalDebugger as that's what its called Experimental. With the language server, its the Language Server, we haven't had one before. Jedi wasn't a language server. Hope that makes sense.

Choose a reason for hiding this comment

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

This must implement an interface. No concrete classes please.

Copy link
Author

Choose a reason for hiding this comment

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

Understood. And ok.

this.initialized = createDeferred<void>();
this.startLanguageServer().then(() => this.initialized.resolve()).ignoreErrors();

this.proposeNewLanguageServerPopup = new ProposeNewLanguageServerBanner(

Choose a reason for hiding this comment

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

Please use DI container to resolve an instance of this class from an interface.

Copy link
Author

Choose a reason for hiding this comment

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

Ok.

traceLogging
},
middleware: {
provideCompletionItem: (document: TextDocument, position: VPosition, token: CancellationToken, next: ProvideCompletionItemsSignature) => {

Choose a reason for hiding this comment

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

Ahhh. Where did you get the information to implement this API. GitHub or Stackoverflow?

Copy link
Author

Choose a reason for hiding this comment

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

}
}

export function getRandom(): number {

Choose a reason for hiding this comment

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

No need to export this, its not used anywhere except in here.

Copy link
Author

Choose a reason for hiding this comment

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

yet

Just left this as exported in case someone would just want the 0.0..1.0 values back, as a safe replacement to Math.random().

Choose a reason for hiding this comment

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

Lets keep it simple, if not required, private scope.

@codecov
Copy link

codecov bot commented Jul 17, 2018

Codecov Report

Merging #2173 into master will decrease coverage by 1.11%.
The diff coverage is 62.71%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2173      +/-   ##
==========================================
- Coverage   79.63%   78.52%   -1.12%     
==========================================
  Files         308      310       +2     
  Lines       14191    14358     +167     
  Branches     2521     2547      +26     
==========================================
- Hits        11301    11274      -27     
- Misses       2878     3072     +194     
  Partials       12       12
Impacted Files Coverage Δ
src/client/debugger/types.ts 100% <ø> (ø) ⬆️
...rc/client/unittests/pytest/services/argsService.ts 86.2% <ø> (ø) ⬆️
src/client/common/utils.ts 57.89% <100%> (+6.37%) ⬆️
src/client/extension.ts 95.42% <100%> (ø) ⬆️
src/client/debugger/banner.ts 80% <100%> (ø) ⬆️
src/client/common/types.ts 100% <100%> (ø) ⬆️
src/client/debugger/serviceRegistry.ts 100% <100%> (ø) ⬆️
src/client/activation/languageServer.ts 26.08% <50%> (-0.03%) ⬇️
...ent/languageServices/languageServerSurveyBanner.ts 51.28% <51.28%> (ø)
...nt/languageServices/proposeLanguageServerBanner.ts 62.5% <62.5%> (ø)
... and 21 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 65ff9e3...0aad7fc. Read the comment docs.

d3r3kk added 13 commits July 17, 2018 20:03
- add new random function to handle 'between n and m'
- still in progress, lots of WIP code yet
- code for setting up new LS started, but needs further work (restart?)
- launch survey for users of new LS works
- NO TESTS YET! GRRRR.
- fix issue with random (pulling 8 bytes from machine id rather than 4)

- update 'shouldShowBanner' a bit, remove DEREK specific flags
- update random functions to actually work properly

- add some comments to the new classes to help define their purpose

- remove debugging/dev code in preparation for going live
- Remove debugging code
- simplify popup classes (no injection necessary)
- pull out common code, put in utils (getRandom)
- correct setting for config to switch to new LS
- Rename ProposeNewLanguageServerBanner to ProposeLanguageServerBanner
- Remove remnant of onNotification.bind.
- Rename the class with the New prefix to lose the New.
- remove export from utils.getRandom.
- Use interfaces for new popups instead of the actual class references.
- Switch back to DI instead of manual construction... (hence the interface switch above).
Copy link

@DonJayamanne DonJayamanne left a comment

Choose a reason for hiding this comment

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

Please do to action my comments.
Also, I believe this (ur work) is still some work in progress.
I'd rather get this PR out asap for tomorrows release.

}

export const IPythonExtensionSurveyBanner = Symbol('IPythonExtensionSurveyBanner');
export interface IPythonExtensionSurveyBanner {

Choose a reason for hiding this comment

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

I see you renamed IExperimenta...Banner and now we have IPythonExtensionSurveyBanner and IPythonExtensionBanner. Why aren't we using the same interface?
If we cannot, then why rename the existing interface to make it generic, when it isn't being re-used?
Anyways, lets not change this now.
In previous commit you seems to have the same API for the debugger and language server banners, and my impression was you'd be using the same Interface, but now we've attempted to create something generic, yet not.
Note, ideally we should have more than one class, one that drives the banner and the other that provides the information about the banner, then the other than acts on the data. I didn't want two-three classes for a simple banner, hence exposed crammed all in one with public methods enabled, initialize, showBanner, shouldShowBanner, disable, launchSurvey, which would ideally work in other banners.

I don't want you to change anything at this stage, lets not do that.

Choose a reason for hiding this comment

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

Please do not change anything, lets just get this PR out.

Copy link
Author

Choose a reason for hiding this comment

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

I agree with your assessment here 100%. I was trying to get to a point where I would have a unified API for all new banners we'll need in the extension going forward.

However, it also became apparent as I was progressing that the 'all in one' API from the original banner wasn't quite what I needed... so I allowed my work to stray somewhat from that, this is the change you are seeing through my submits.

I was learning other things while also attempting this and simply ran short on time to make things as nice as I'd otherwise like. I have a few clean-up tasks on my plate once this release goes out for sure!

...lets just get this PR out.

Agreed. It appears I have some other issues to concern myself with from what the build machines are telling me!

this.maxShowAttempts = maxShowAttemptThreshold;
}

public get optionLabels(): string[] {

Choose a reason for hiding this comment

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

I'd rename this to options. No need to change.

Choose a reason for hiding this comment

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

Not sure where these fit, we're exposing this as well as bannerLabels...
Anyways, the code might not yet be complete.

Choose a reason for hiding this comment

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

Please do not change

Copy link
Author

Choose a reason for hiding this comment

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

Yep, sussing out what is common between the banners here. This will be a bit of a WIP until next release unfortunately.

return this.getPythonLSLaunchCounter();
}

public optionTriggerCount(label: string): number {

Choose a reason for hiding this comment

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

Feels like you need a better data structure, or just use enums instead of this.
This feels clunky.

Copy link
Author

Choose a reason for hiding this comment

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

Agreed. This was me coming up with ideas for unit tests...

let count: number = -1;
switch (label) {
case this.bannerLabels[LSSurveyLabelIndex.Yes]: {
count = this.labelTriggerCount[LSSurveyLabelIndex.Yes];

Choose a reason for hiding this comment

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

In the future please use an Enum, more readable, than numbers.
Please do not change now.

Copy link
Author

Choose a reason for hiding this comment

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

Not entirely certain what you mean here... I'm using an Enum for the indecies into my two arrays.

Copy link

@DonJayamanne DonJayamanne left a comment

Choose a reason for hiding this comment

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

Please do write tests for the new code.
Also if the banner interface is not generic, then please revert the name of the interface used by the experimental debugger.
Please rename only if all three banners are using the same interface.

@d3r3kk d3r3kk closed this Jul 18, 2018
@d3r3kk d3r3kk reopened this Jul 18, 2018
@d3r3kk
Copy link
Author

d3r3kk commented Jul 18, 2018

@DonJayamanne & @brettcannon:
Whelp. I'm having a bit of trouble this morning. The build is broken for my change, sure. But locally, the build is broken when I clean everything and build what is on master as well.

First, the pytest lib was updated to include new commands, so that breaks (I knew about that and accounted for it in my changes above). That fails for me locally, but not when I build master on our CI? Other failures occur as well:

  2086 passing (10m)
  170 pending
  19 failing

  1) Exclude files (Language Server)
       Default exclusions:
      AssertionError: false == true
      + expected - actual
      -false
      +true
      at Object.<anonymous> (src/test/activation/excludeFiles.ls.test.ts:67:16)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/activation/excludeFiles.ls.test.js:6:58)
      at <anonymous>
  2) Hover Definition (Language Server)
       Across files:
      AssertionError: Start position is incorrect
      + expected - actual
      -1,9
      +1,0
      at Object.<anonymous> (src/test/definitions/hover.ls.test.ts:66:16)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/hover.ls.test.js:6:58)
      at <anonymous>
  3) Hover Definition (Language Server)
       Highlighting Class:
      AssertionError: Start position is incorrect
      + expected - actual
      -11,12
      +11,7
      at Object.<anonymous> (src/test/definitions/hover.ls.test.ts:135:16)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/hover.ls.test.js:6:58)
      at <anonymous>
  4) Hover Definition (Language Server)
       Highlight Method:
      AssertionError: Start position is incorrect
      + expected - actual
      -12,5
      +12,0
      at Object.<anonymous> (src/test/definitions/hover.ls.test.ts:158:16)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/hover.ls.test.js:6:58)
      at <anonymous>
  5) Hover Definition (Language Server)
       Highlight Function:
      AssertionError: incorrect number of lines
      + expected - actual
      -4
      +5
      at verifySignatureLines (src/test/definitions/hover.ls.test.ts:234:16)
      at Object.<anonymous> (src/test/definitions/hover.ls.test.ts:186:9)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/hover.ls.test.js:6:58)
      at <anonymous>
  6) Hover Definition (Language Server)
       Highlight Multiline Method Signature:
      AssertionError: Start position is incorrect
      + expected - actual
      -14,9
      +14,4
      at Object.<anonymous> (src/test/definitions/hover.ls.test.ts:192:16)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/hover.ls.test.js:6:58)
      at <anonymous>
  7) Definition Navigation
       From own definition:
      AssertionError: Start position is incorrect
      + expected - actual
      -2,0
      +2,4
      at assertRange (src/test/definitions/navigation.test.ts:32:16)
      at Object.<anonymous> (src/test/definitions/navigation.test.ts:47:17)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/navigation.test.js:5:58)
      at <anonymous>
  8) Definition Navigation
       Nested function:
      AssertionError: Start position is incorrect
      + expected - actual
      -6,4
      +6,8
      at assertRange (src/test/definitions/navigation.test.ts:32:16)
      at Object.<anonymous> (src/test/definitions/navigation.test.ts:47:17)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/navigation.test.js:5:58)
      at <anonymous>
  9) Definition Navigation
       Decorator usage:
      AssertionError: Start position is incorrect
      + expected - actual
      -2,0
      +2,4
      at assertRange (src/test/definitions/navigation.test.ts:32:16)
      at Object.<anonymous> (src/test/definitions/navigation.test.ts:47:17)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/navigation.test.js:5:58)
      at <anonymous>
  10) Definition Navigation
       Function decorated by stdlib:
      AssertionError: Start position is incorrect
      + expected - actual
      -21,0
      +21,4
      at assertRange (src/test/definitions/navigation.test.ts:32:16)
      at Object.<anonymous> (src/test/definitions/navigation.test.ts:47:17)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/navigation.test.js:5:58)
      at <anonymous>
  11) Definition Navigation
       Function decorated by local decorator:
      AssertionError: Start position is incorrect
      + expected - actual
      -14,0
      +14,4
      at assertRange (src/test/definitions/navigation.test.ts:32:16)
      at Object.<anonymous> (src/test/definitions/navigation.test.ts:47:17)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/navigation.test.js:5:58)
      at <anonymous>
  12) Definition Navigation
       Module imported decorator usage:
      AssertionError: Start position is incorrect
      + expected - actual
      -2,0
      +2,4
      at assertRange (src/test/definitions/navigation.test.ts:32:16)
      at Object.<anonymous> (src/test/definitions/navigation.test.ts:47:17)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/navigation.test.js:5:58)
      at <anonymous>
  13) Definition Navigation
       Module imported function decorated by stdlib:
      AssertionError: Start position is incorrect
      + expected - actual
      -21,0
      +21,4
      at assertRange (src/test/definitions/navigation.test.ts:32:16)
      at Object.<anonymous> (src/test/definitions/navigation.test.ts:47:17)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/navigation.test.js:5:58)
      at <anonymous>
  14) Definition Navigation
       Module imported function decorated by local decorator:
      AssertionError: Start position is incorrect
      + expected - actual
      -14,0
      +14,4
      at assertRange (src/test/definitions/navigation.test.ts:32:16)
      at Object.<anonymous> (src/test/definitions/navigation.test.ts:47:17)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/navigation.test.js:5:58)
      at <anonymous>
  15) Definition Navigation
       Specifically imported decorator usage:
      AssertionError: Start position is incorrect
      + expected - actual
      -2,0
      +2,4
      at assertRange (src/test/definitions/navigation.test.ts:32:16)
      at Object.<anonymous> (src/test/definitions/navigation.test.ts:47:17)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/navigation.test.js:5:58)
      at <anonymous>
  16) Definition Navigation
       Specifically imported function decorated by stdlib:
      AssertionError: Start position is incorrect
      + expected - actual
      -21,0
      +21,4
      at assertRange (src/test/definitions/navigation.test.ts:32:16)
      at Object.<anonymous> (src/test/definitions/navigation.test.ts:47:17)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/navigation.test.js:5:58)
      at <anonymous>
  17) Definition Navigation
       Specifically imported function decorated by local decorator:
      AssertionError: Start position is incorrect
      + expected - actual
      -14,0
      +14,4
      at assertRange (src/test/definitions/navigation.test.ts:32:16)
      at Object.<anonymous> (src/test/definitions/navigation.test.ts:47:17)
      at Generator.next (<anonymous>)
      at fulfilled (out/test/definitions/navigation.test.js:5:58)
      at <anonymous>
  18) Unit Tests - argsService
       pytest
         Check for new/unrecognized options with values:
      AssertionError: Options not found
      + expected - actual
      +--cov, --cov-config, --cov-fail-under, --cov-report
      at Context.test (src/test/unittests/argsService.unit.test.ts:77:25)
  19) Unit Tests - argsService
       pytest
         Check for new/unrecognized options without values:
      AssertionError: Options not found
      + expected - actual
      +--cov-append, --cov-branch, --no-cov, --no-cov-on-fail
      at Context.test (src/test/unittests/argsService.unit.test.ts:85:25)

(18) and (19) are the failures due to pytest being updated. Everything else seems at least language-server adjacent...

I'm going to reset everything I can, or will simply try on a freshly paved VM this morning, but I'm not feeling confident for the release at all.

shownCount: Promise<number>;
optionLabels: string[];
showBanner(): Promise<void>;
optionTriggerCount(label: string): number;

Choose a reason for hiding this comment

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

This isn't used.

d3r3kk added 4 commits July 18, 2018 12:04
- Remove notion of 'trigger count' from popup banners altogether
  - my anti-pester idea was rejected for good reason
  - user has 'No' button to dismiss these popups forever
   - remove shownCount from  propose LanguageServer popup
- Put the 'IExperimentalDebuggerBanner' name back the way it was
- wrap it in a check for 'is test execution'
- we need to block the popup during testing for some reason (TBD)
@DonJayamanne DonJayamanne changed the title [WIP] Add two new popups: One to switch to new LS, another to ask for feedback. Add two new popups: One to switch to new LS, another to ask for feedback. Jul 18, 2018
@DonJayamanne DonJayamanne merged commit d724f74 into microsoft:master Jul 18, 2018
@d3r3kk d3r3kk deleted the langservpopup branch July 18, 2018 22:28
bschley pushed a commit to bschley/vscode-python that referenced this pull request Aug 2, 2018
@lock lock bot locked as resolved and limited conversation to collaborators Jul 31, 2019
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.

Prompt users to try and give feedback on the language server

2 participants