Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Convert atom-languageclient to TypeScript#175

Merged
daviwil merged 18 commits intomasterfrom
typescript-conversion
Feb 14, 2018
Merged

Convert atom-languageclient to TypeScript#175
daviwil merged 18 commits intomasterfrom
typescript-conversion

Conversation

@daviwil
Copy link
Contributor

@daviwil daviwil commented Feb 7, 2018

This PR is an experiment to see how much effort it'd be to convert the codebase to TypeScript. Using TypeScript here would help us dogfood the ide-typescript package.

  • Investigate ownership of official atom typings
  • Investigate publishing atom-ide typings

@daviwil daviwil requested a review from damieng February 7, 2018 18:00
lib/convert.ts Outdated
import * as ls from './languageclient';
import {Point, Range} from 'atom';
import {Point, ProjectFileEvent, Range, TextEditor} from 'atom';
import {Diagnostic, DiagnosticType, TextEdit} from '../typings/atom-ide';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ideally we wouldn't import from ../typings/atom-ide but instead from a "fake" atom-ide module provided by a @types/atom-ide package.

lib/convert.ts Outdated
return ls.DiagnosticSeverity.Information;
default:
(type: empty);
// (type: empty);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This line gave an error in the TypeScript compiler, not sure what purpose it serves.

Copy link

Choose a reason for hiding this comment

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

Assuming that the purpose was to assert that the switch was exhaustive over DiagnosticType, one way of writing this in typescript is:

const never: never = type;
throw Error(`Unexpected diagnostic type ${never}`);

The never type is a type with no legal values, so that assignment will fail if type inference thinks that there are any legal values for type at that point in the program.

(Using never instead of types in the error ensures that it doesn't look like never is an unused variable when linting or compiling)


/* declare module 'atom-ide' {*/

export type OutlineProvider = {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This file is a conversion of the flowtype definitions for Atom-IDE and isn't finished yet. There are some type system differences that I still need to account for.

lib/convert.ts Outdated
return ls.DiagnosticSeverity.Information;
default:
(type: empty);
// (type: empty);
Copy link

Choose a reason for hiding this comment

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

Assuming that the purpose was to assert that the switch was exhaustive over DiagnosticType, one way of writing this in typescript is:

const never: never = type;
throw Error(`Unexpected diagnostic type ${never}`);

The never type is a type with no legal values, so that assignment will fail if type inference thinks that there are any legal values for type at that point in the program.

(Using never instead of types in the error ensures that it doesn't look like never is an unused variable when linting or compiling)

lib/convert.ts Outdated
//
// Returns an {Array} of Atom {TextEdit} objects.
static convertLsTextEdits(textEdits: ?Array<ls.TextEdit>): Array<atomIde$TextEdit> {
static convertLsTextEdits(textEdits: Array<ls.TextEdit>): Array<TextEdit> {
Copy link

Choose a reason for hiding this comment

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

The TypeScript representation of ?Array<ls.TextEdit> is Array<ls.TextEdit> | null | undefined I think (unless you're not compiling with strict null checks)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing that out!

"outDir": "build",
"lib": ["es6", "dom"],
"inlineSources": true,
"inlineSourceMap": true,
Copy link

Choose a reason for hiding this comment

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

"strict": true, will catch many more errors, though it also has more false positives

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! I think we'll turn on strictness once we make a bit more progress

@daviwil
Copy link
Contributor Author

daviwil commented Feb 10, 2018

Thanks for the help @rictic!

@daviwil
Copy link
Contributor Author

daviwil commented Feb 14, 2018

All files are converted now and tests (except for one commented out file) are passing! Next steps are to finish up some small conversion tasks and then start cleaning up the code based on tslint results.

@daviwil daviwil changed the title WIP: TypeScript conversion experiment Convert atom-languageclient to TypeScript Feb 14, 2018
@daviwil
Copy link
Contributor Author

daviwil commented Feb 14, 2018

This PR is ready to be reviewed! I'll sit down with @damieng when he's back and go over everything since this is a pretty big change. Everyone else should feel free to ask questions!

@damieng
Copy link
Contributor

damieng commented Feb 14, 2018

I'm going to have some time this morning so let's do it then! I'll schedule with you on slack.

Copy link
Contributor Author

@daviwil daviwil left a comment

Choose a reason for hiding this comment

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

Adding comments from review with @damieng

// Sort edits in reverse order to prevent edit conflicts.
edits.sort((edit1, edit2) => -edit1.oldRange.compare(edit2.oldRange));
const buffer = editor.getBuffer();
const buffer = (editor as any).getBuffer() as TextBuffer;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Need to make this public in Atom APIs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Turns out it is public and in the Atom type defs, fixable on our side

suggestion.descriptionMarkdown = item.documentation;

if (typeof item.documentation === 'object') {
suggestion.descriptionMarkdown = item.documentation.value;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Check issues to see if this was done based on an issue filed

return [];
}
invariant(serverCapabilities.codeActionProvider, 'Must have the textDocument/codeAction capability');
assert(serverCapabilities.codeActionProvider, 'Must have the textDocument/codeAction capability');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

File issue for using assert(canAdapt) more broadly in adapters

if (typeof markedString === 'string') {
return { type: 'markdown', value: markedString };
}
else {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Get rid of unnecessary elses

// ChildProcess. This is used so that language packages with alternative
// language server process hosting strategies can return something compatible
// with AutoLanguageClient.startServerProcess.
export interface LanguageServerProcess extends EventEmitter {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do this in a separate PR

//
// Returns a {Promise} that will accept when complete.
export default (async function downloadFile(
export default (async function DownloadFile(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Change this back to lower case

}
}

/**
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Move DiagnosticCode here

lib/logger.ts Outdated
}

public warn(...args: any[]) {
// tslint:disable-next-line:no-console
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can we disable this at file level?

lib/main.ts Outdated
LinterPushV2Adapter,
};
export * from './auto-languageclient';
export { AutoLanguageClient, DownloadFile, Convert };
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Add Linter

}
}
],
"atomTestRunner": "./build/test/runner",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Make sure this works in Atom test runner

@daviwil
Copy link
Contributor Author

daviwil commented Feb 14, 2018

Updated the PR based on feedback and got tests running inside of Atom again. Let me know if there's anything else you'd like me to do before merging!


// Public: Retrieves signature help for a given editor and position.
getSignatureHelp(editor: atom$TextEditor, point: atom$Point): Promise<?atomIde$SignatureHelp> {
public getSignatureHelp(editor: TextEditor, point: Point): SignatureHelp | Promise<SignatureHelp> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't it be Promise<lsp.SignatureHelp | null>?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, thanks!

@daviwil daviwil merged commit 156b306 into master Feb 14, 2018
@daviwil daviwil deleted the typescript-conversion branch February 14, 2018 22:08
@laughedelic
Copy link
Contributor

laughedelic commented Feb 14, 2018

Could you clarify why all types ?Foo became Foo | null and not Foo | undefined | null or Foo | undefined? (I'm asking because I saw #175 (comment) and read the same somewhere else)

@daviwil
Copy link
Contributor Author

daviwil commented Feb 14, 2018

I suppose Foo | undefined | null would be more accurate. I think I misread something in the TypeScript docs when I chose to do it that way. I'll get this fixed soon!

@laughedelic
Copy link
Contributor

Cool! I just read about --strictNullChecks and it seem quite useful in this case.

@daviwil
Copy link
Contributor Author

daviwil commented Feb 14, 2018

Yep, I'm about 80% of the way through updating the codebase to be checked with that :)

@damieng
Copy link
Contributor

damieng commented Feb 15, 2018 via email

@daviwil
Copy link
Contributor Author

daviwil commented Feb 15, 2018

Yep, I'm not adding undefined in places where null is returned on purpose to indicate a lack of result.

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.

4 participants