-
Notifications
You must be signed in to change notification settings - Fork 73
Conversation
Codecov Report
@@ Coverage Diff @@
## master #186 +/- ##
==========================================
+ Coverage 54.65% 55.54% +0.89%
==========================================
Files 15 16 +1
Lines 1945 1991 +46
Branches 311 319 +8
==========================================
+ Hits 1063 1106 +43
+ Misses 764 763 -1
- Partials 118 122 +4
Continue to review full report at Codecov.
|
Thanks for the PR! It is quite hard for me to understand what is going on here (some documentaiton on DiagnosticsPublisher would be helpful). What I got:
Problems I have:
No reason, makes sense to me. I think it didn't matter because we read all files in anyway and all operations ensured the files they needed.
I wouldn't be too worried about that, I don't have a problem with showing diagnostics for closed files.
I would expect the client to not spam |
680099b
to
807ec7b
Compare
Please ping when this is ready for another review |
469e0ef
to
d13ed60
Compare
d13ed60
to
bb0210a
Compare
I've addressed points from the initial review, thanks for taking the time!
I have some time to work on this, but feel free to work on my branch! |
Ping @felixfbecker, ready for another review! |
src/diagnostics.ts
Outdated
const diagnosticsByFile = this.groupByFile(diagnostics); | ||
|
||
// add empty diagnostics for fixed files, so client marks them as resolved | ||
this.problemFiles.forEach(file => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use for of
loops
src/diagnostics.ts
Outdated
* @param diagnostics All diagnostics received in an update | ||
*/ | ||
private groupByFile(diagnostics: ts.Diagnostic[]): Map<string, ts.Diagnostic[]> { | ||
const diagnosticsByFile: Map<string, ts.Diagnostic[]> = new Map(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer const diagnosticsByFile = new Map<string, ts.Diagnostic[]>();
src/project-manager.ts
Outdated
config.syncProgram(span); | ||
config.ensureConfigFile(); | ||
|
||
// If file was unknown, we add it (which syncs program) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How can a didChange
ever happen without a didOpen
?
src/project-manager.ts
Outdated
if (!program) { | ||
return false; | ||
} | ||
let changed = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this variable is unneeded
src/project-manager.ts
Outdated
* Ensures a single file is available to the LanguageServiceHost | ||
* @param filePath | ||
*/ | ||
ensureSourceFile(filePath: string): boolean { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I understand it, the return type indicates whether the program was resynced as part of the method call? Why exactly is that information needed for a caller who wants to ensure a source file? If it is needed to decide whether to republish diagnostics, then this puts unnecessary burden on the callers to always check the return value and act on it - emitting an event is better in that case
src/test/diagnostics.test.ts
Outdated
|
||
beforeEach(() => { | ||
publishSpy = sinon.spy(); | ||
langClient = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you want to stub all and only spy on one I would use sinon.createStubInstance(RemoteLanguageClient)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome!
src/test/diagnostics.test.ts
Outdated
}); | ||
|
||
// Integration tests that include TS compilation. | ||
describe('Diagnostics', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still not convinced we should have these here.
The expected behaviour of the ProjectManager is to call the DiagnosticHandler, so there should be tests in the ProjectManager suite that assert it is called. That's where all the setup/teardown code is located.
All tests are to prevent regressions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right you are, these have been replaced with some unit tests on didChange/didOpen.
@felixfbecker thanks for the guidance, hope the didChange and test changes are what you intended! |
src/project-manager.ts
Outdated
config.getHost().incProjectVersion(); | ||
config.syncProgram(span); | ||
config.syncProgram(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why the removal of the span?
src/diagnostics.ts
Outdated
this.problemFiles.clear(); | ||
|
||
// for each file: publish and set as problem file | ||
diagnosticsByFile.forEach((diagnostics, file) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for of
src/test/diagnostics.test.ts
Outdated
|
||
describe('DiagnosticsPublisher', () => { | ||
let langClient: LanguageClient; | ||
let diagnosticsManager; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no type
src/test/diagnostics.test.ts
Outdated
let langClient: LanguageClient; | ||
let diagnosticsManager; | ||
const createTSFileDiagnostic = (message: string, file: ts.SourceFile) => { | ||
return { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use arrow function without return or named function
src/test/diagnostics.test.ts
Outdated
beforeEach(() => { | ||
publishSpy = sinon.spy(); | ||
langClient = sinon.createStubInstance(RemoteLanguageClient); | ||
langClient.textDocumentPublishDiagnostics = publishSpy; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all methods are stubs now, so no need to assign the spy here. Just type the langClient as something like
{ [K in keyof LanguageClient]: LanguageClient[K] & sinon.SinonStub }
// or
LanguageClient & { textDocumentPublishDiagnostics: Function & sinon.SinonSpy }
src/test/project-manager.test.ts
Outdated
it('should compile opened file and return diagnostics', async () => { | ||
projectManager.didOpen('file:///src/dummy.ts', 'const num: number = "banana";'); | ||
const lastArgs = diagnosticsSpy.lastCall.args[0]; | ||
assert.equal(lastArgs.length, 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure there is a sinon assert function for this. Should also first assert that the function is called at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added an assert as suggested, but I couldn't find a way to assert on the last call only with sinon's asserts. They suggest using sinon.assert.calledWith
, and we would need a sinon.match
to avoid check all of TS's diagnostics type. I tried this briefly but ran out of time, let me know if you want me to switch it to this syntax.
src/typescript-service.ts
Outdated
@@ -67,7 +68,6 @@ export type TypeScriptServiceFactory = (client: LanguageClient, options?: TypeSc | |||
* underscore. | |||
*/ | |||
export class TypeScriptService { | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
revert
src/diagnostics.ts
Outdated
* Requires a connection to the remote client to send diagnostics to | ||
* @param remoteClient | ||
*/ | ||
constructor(private remoteClient: LanguageClient) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to client
src/diagnostics.ts
Outdated
* The files that were last reported to have errors | ||
* If they don't appear in the next update, we must publish empty diagnostics for them. | ||
*/ | ||
private problemFiles: Set<string> = new Set(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefer type inference
src/diagnostics.ts
Outdated
const diagnosticsByFile = new Map<string, ts.Diagnostic[]>(); | ||
for (const diagnostic of diagnostics) { | ||
// TODO for some reason non-file diagnostics end up in here (where file is undefined) | ||
const fileName = diagnostic.file ? diagnostic.file.fileName : ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const fileName = diagnostic.file && diagnostic.file.fileName
Thanks for the review, apologies for the unintended changes! |
Thanks! Great work |
This is a small implementation towards #162:
ExposeprogramSyncEvents
on ProjectConfiguration, triggered after compilationDiagnosticsPublisher requests diagnostics on a synced program and publishes per fileBasic testsThanks for the answers on these! Continuing conversation below.