-
Notifications
You must be signed in to change notification settings - Fork 101
Adds a CodeAction provider to support fixits #40
Changes from 2 commits
b8063f9
559bc52
0dd1066
e86c4f0
c6ad823
91e9a45
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -163,13 +163,16 @@ export class DiagnostcsAdapter extends Adapter { | |
return null; | ||
} | ||
const promises: Promise<ts.Diagnostic[]>[] = []; | ||
const { noSyntaxValidation, noSemanticValidation } = this._defaults.getDiagnosticsOptions(); | ||
const { noSyntaxValidation, noSemanticValidation, noSuggestionDiagnostics } = this._defaults.getDiagnosticsOptions(); | ||
if (!noSyntaxValidation) { | ||
promises.push(worker.getSyntacticDiagnostics(resource.toString())); | ||
} | ||
if (!noSemanticValidation) { | ||
promises.push(worker.getSemanticDiagnostics(resource.toString())); | ||
} | ||
if (!noSuggestionDiagnostics) { | ||
promises.push(worker.getSuggestionDiagnostics(resource.toString())); | ||
} | ||
return Promise.all(promises); | ||
}).then(diagnostics => { | ||
if (!diagnostics || !monaco.editor.getModel(resource)) { | ||
|
@@ -191,14 +194,24 @@ export class DiagnostcsAdapter extends Adapter { | |
const { lineNumber: endLineNumber, column: endColumn } = this._offsetToPosition(resource, diag.start + diag.length); | ||
|
||
return { | ||
severity: monaco.MarkerSeverity.Error, | ||
severity: this._tsDiagnosticCategoryToMarkerSeverity(diag.category), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously we'd only show only errors, now it can be of many types |
||
startLineNumber, | ||
startColumn, | ||
endLineNumber, | ||
endColumn, | ||
message: flattenDiagnosticMessageText(diag.messageText, '\n') | ||
message: flattenDiagnosticMessageText(diag.messageText, '\n'), | ||
code: diag.code.toString() | ||
}; | ||
} | ||
|
||
private _tsDiagnosticCategoryToMarkerSeverity(category: ts.DiagnosticCategory): monaco.MarkerSeverity { | ||
switch(category) { | ||
case ts.DiagnosticCategory.Error: return monaco.MarkerSeverity.Error | ||
case ts.DiagnosticCategory.Message: return monaco.MarkerSeverity.Info | ||
case ts.DiagnosticCategory.Warning: return monaco.MarkerSeverity.Warning | ||
case ts.DiagnosticCategory.Suggestion: return monaco.MarkerSeverity.Hint | ||
} | ||
} | ||
} | ||
|
||
// --- suggest ------ | ||
|
@@ -632,6 +645,64 @@ export class FormatOnTypeAdapter extends FormatHelper implements monaco.language | |
} | ||
} | ||
|
||
// --- code actions ------ | ||
|
||
export class CodeActionAdaptor extends FormatHelper implements monaco.languages.CodeActionProvider { | ||
|
||
public provideCodeActions(model: monaco.editor.ITextModel, range: Range, context: monaco.languages.CodeActionContext, token: CancellationToken): Promise<monaco.languages.CodeActionList> { | ||
const resource = model.uri; | ||
|
||
return this._worker(resource).then(worker => { | ||
const start = this._positionToOffset(resource, { lineNumber: range.startLineNumber, column: range.startColumn }); | ||
const end = this._positionToOffset(resource, { lineNumber: range.endLineNumber, column: range.endColumn }); | ||
|
||
// TODO: where to get the current formatting options from? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one I'm still not sure of, it doesn't feel critical - but if anyone has ideas on what the right way to pass in the formatting options, I'd love to know them |
||
const formatOptions = FormatHelper._convertOptions({insertSpaces: true, tabSize: 2}); | ||
const errorCodes = context.markers.filter(m => m.code).map(m => m.code).map(Number); | ||
|
||
return worker.getCodeFixesAtPosition(resource.toString(), start, end, errorCodes, formatOptions); | ||
|
||
}).then(codeFixes => { | ||
|
||
return codeFixes.filter(fix => { | ||
// Removes any 'make a new file'-type code fix | ||
return fix.changes.filter(change => change.isNewFile).length === 0; | ||
}).map(fix => { | ||
return this._tsCodeFixActionToMonacoCodeAction(model, context, fix); | ||
}) | ||
}).then(result => { | ||
return { | ||
actions: result, | ||
dispose: () => {} | ||
}; | ||
}); | ||
} | ||
|
||
|
||
private _tsCodeFixActionToMonacoCodeAction(model: monaco.editor.ITextModel, context: monaco.languages.CodeActionContext, codeFix: ts.CodeFixAction): monaco.languages.CodeAction { | ||
const edits: monaco.languages.ResourceTextEdit[] = codeFix.changes.map(edit => ({ | ||
resource: model.uri, | ||
edits: edit.textChanges.map(tc => ({ | ||
range: this._textSpanToRange(model.uri, tc.span), | ||
text: tc.newText | ||
})) | ||
})); | ||
|
||
const action: monaco.languages.CodeAction = { | ||
title: codeFix.description, | ||
edit: { edits: edits }, | ||
diagnostics: context.markers, | ||
command: { | ||
id: codeFix.fixName, | ||
title: codeFix.description, | ||
tooltip: codeFix.description | ||
}, | ||
kind: codeFix.fixName | ||
}; | ||
|
||
return action; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is my best-guess implementation, leaving most of the work to the edits which come with the original TS code action ideas |
||
} | ||
} | ||
// --- rename ---- | ||
|
||
export class RenameAdapter extends Adapter implements monaco.languages.RenameProvider { | ||
|
@@ -675,4 +746,4 @@ export class RenameAdapter extends Adapter implements monaco.languages.RenamePro | |
|
||
return { edits }; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -146,6 +146,12 @@ export class TypeScriptWorker implements ts.LanguageServiceHost { | |
return Promise.resolve(diagnostics); | ||
} | ||
|
||
getSuggestionDiagnostics(fileName: string): Promise<ts.DiagnosticWithLocation[]> { | ||
const diagnostics = this._languageService.getSuggestionDiagnostics(fileName); | ||
TypeScriptWorker.clearFiles(diagnostics); | ||
return Promise.resolve(diagnostics); | ||
} | ||
|
||
getCompilerOptionsDiagnostics(fileName: string): Promise<ts.Diagnostic[]> { | ||
const diagnostics = this._languageService.getCompilerOptionsDiagnostics(); | ||
TypeScriptWorker.clearFiles(diagnostics); | ||
|
@@ -208,6 +214,11 @@ export class TypeScriptWorker implements ts.LanguageServiceHost { | |
return Promise.resolve(this._languageService.getEmitOutput(fileName)); | ||
} | ||
|
||
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes:number[], formatOptions: ts.FormatCodeOptions): Promise<ReadonlyArray<ts.CodeFixAction>> { | ||
const preferences = {} | ||
return Promise.resolve(this._languageService.getCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think its worth exposing all of the TS code fix options to devs, but I'm open to changing that? it looks like this: interface UserPreferences {
readonly disableSuggestions?: boolean;
readonly quotePreference?: "auto" | "double" | "single";
readonly includeCompletionsForModuleExports?: boolean;
readonly includeCompletionsWithInsertText?: boolean;
readonly importModuleSpecifierPreference?: "relative" | "non-relative";
/** Determines whether we import `foo/index.ts` as "foo", "foo/index", or "foo/index.js" */
readonly importModuleSpecifierEnding?: "minimal" | "index" | "js";
readonly allowTextChangesInNewFiles?: boolean;
readonly providePrefixAndSuffixTextForRename?: boolean;
} |
||
} | ||
|
||
updateExtraLibs(extraLibs: IExtraLibs) { | ||
this._extraLibs = extraLibs; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -165,10 +165,11 @@ <h2>Monaco Editor TypeScript test page</h2> | |
'var game = new Conway.GameOfLife();', | ||
|
||
].join('\n'), | ||
language: 'typescript' | ||
language: 'typescript', | ||
lightbulb: { enabled: true } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Took 30m guessing, then 5m with a debugger on the plane to figure this one out! 🍡 💡 |
||
}); | ||
}); | ||
</script> | ||
|
||
</body> | ||
</html> | ||
</html> |
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 also means that JS files should get suggestions too