|
|
@ -7,6 +7,7 @@
|
|
|
|
import { LanguageServiceDefaultsImpl } from './monaco.contribution';
|
|
|
|
import { LanguageServiceDefaultsImpl } from './monaco.contribution';
|
|
|
|
import * as ts from './lib/typescriptServices';
|
|
|
|
import * as ts from './lib/typescriptServices';
|
|
|
|
import { TypeScriptWorker } from './tsWorker';
|
|
|
|
import { TypeScriptWorker } from './tsWorker';
|
|
|
|
|
|
|
|
import { libFileSet } from "./lib/lib.index"
|
|
|
|
|
|
|
|
|
|
|
|
import Uri = monaco.Uri;
|
|
|
|
import Uri = monaco.Uri;
|
|
|
|
import Position = monaco.Position;
|
|
|
|
import Position = monaco.Position;
|
|
|
@ -58,7 +59,7 @@ function displayPartsToString(displayParts: ts.SymbolDisplayPart[] | undefined):
|
|
|
|
|
|
|
|
|
|
|
|
export abstract class Adapter {
|
|
|
|
export abstract class Adapter {
|
|
|
|
|
|
|
|
|
|
|
|
constructor(protected _worker: (first: Uri, ...more: Uri[]) => Promise<TypeScriptWorker>) {
|
|
|
|
constructor(protected _worker: (...uris: Uri[]) => Promise<TypeScriptWorker>) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// protected _positionToOffset(model: monaco.editor.ITextModel, position: monaco.IPosition): number {
|
|
|
|
// protected _positionToOffset(model: monaco.editor.ITextModel, position: monaco.IPosition): number {
|
|
|
@ -78,6 +79,75 @@ export abstract class Adapter {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- lib files
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class LibFiles {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private _libFiles: Record<string, string>;
|
|
|
|
|
|
|
|
private _hasFetchedLibFiles: boolean;
|
|
|
|
|
|
|
|
private _fetchLibFilesPromise: Promise<void> | null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
|
|
|
private readonly _worker: (...uris: Uri[]) => Promise<TypeScriptWorker>
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
this._libFiles = {};
|
|
|
|
|
|
|
|
this._hasFetchedLibFiles = false;
|
|
|
|
|
|
|
|
this._fetchLibFilesPromise = null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public isLibFile(uri: Uri | null): boolean {
|
|
|
|
|
|
|
|
if (!uri) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uri.path.indexOf("/lib.") === 0) {
|
|
|
|
|
|
|
|
return !!libFileSet[uri.path.slice(1)];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public getOrCreateModel(uri: Uri): monaco.editor.ITextModel | null {
|
|
|
|
|
|
|
|
const model = monaco.editor.getModel(uri);
|
|
|
|
|
|
|
|
if (model) {
|
|
|
|
|
|
|
|
return model;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.isLibFile(uri) && this._hasFetchedLibFiles) {
|
|
|
|
|
|
|
|
return monaco.editor.createModel(this._libFiles[uri.path.slice(1)], "javascript", uri);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private _containsLibFile(uris: (Uri | null)[]): boolean {
|
|
|
|
|
|
|
|
for (let uri of uris) {
|
|
|
|
|
|
|
|
if (this.isLibFile(uri)) {
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async fetchLibFilesIfNecessary(uris: (Uri | null)[]): Promise<void> {
|
|
|
|
|
|
|
|
if (!this._containsLibFile(uris)) {
|
|
|
|
|
|
|
|
// no lib files necessary
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
await this._fetchLibFiles();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private _fetchLibFiles(): Promise<void> {
|
|
|
|
|
|
|
|
if (!this._fetchLibFilesPromise) {
|
|
|
|
|
|
|
|
this._fetchLibFilesPromise = (
|
|
|
|
|
|
|
|
this._worker()
|
|
|
|
|
|
|
|
.then(w => w.getLibFiles())
|
|
|
|
|
|
|
|
.then((libFiles) => {
|
|
|
|
|
|
|
|
this._hasFetchedLibFiles = true;
|
|
|
|
|
|
|
|
this._libFiles = libFiles;
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._fetchLibFilesPromise;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- diagnostics --- ---
|
|
|
|
// --- diagnostics --- ---
|
|
|
|
|
|
|
|
|
|
|
|
enum DiagnosticCategory {
|
|
|
|
enum DiagnosticCategory {
|
|
|
@ -92,8 +162,11 @@ export class DiagnosticsAdapter extends Adapter {
|
|
|
|
private _disposables: IDisposable[] = [];
|
|
|
|
private _disposables: IDisposable[] = [];
|
|
|
|
private _listener: { [uri: string]: IDisposable } = Object.create(null);
|
|
|
|
private _listener: { [uri: string]: IDisposable } = Object.create(null);
|
|
|
|
|
|
|
|
|
|
|
|
constructor(private _defaults: LanguageServiceDefaultsImpl, private _selector: string,
|
|
|
|
constructor(
|
|
|
|
worker: (first: Uri, ...more: Uri[]) => Promise<TypeScriptWorker>
|
|
|
|
private readonly _libFiles: LibFiles,
|
|
|
|
|
|
|
|
private _defaults: LanguageServiceDefaultsImpl,
|
|
|
|
|
|
|
|
private _selector: string,
|
|
|
|
|
|
|
|
worker: (...uris: Uri[]) => Promise<TypeScriptWorker>
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
super(worker);
|
|
|
|
super(worker);
|
|
|
|
|
|
|
|
|
|
|
@ -180,19 +253,31 @@ export class DiagnosticsAdapter extends Adapter {
|
|
|
|
promises.push(worker.getSuggestionDiagnostics(model.uri.toString()));
|
|
|
|
promises.push(worker.getSuggestionDiagnostics(model.uri.toString()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const diagnostics = await Promise.all(promises);
|
|
|
|
const allDiagnostics = await Promise.all(promises);
|
|
|
|
|
|
|
|
|
|
|
|
if (!diagnostics || model.isDisposed()) {
|
|
|
|
if (!allDiagnostics || model.isDisposed()) {
|
|
|
|
// model was disposed in the meantime
|
|
|
|
// model was disposed in the meantime
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const markers = diagnostics
|
|
|
|
const diagnostics = allDiagnostics
|
|
|
|
.reduce((p, c) => c.concat(p), [])
|
|
|
|
.reduce((p, c) => c.concat(p), [])
|
|
|
|
.filter(d => (this._defaults.getDiagnosticsOptions().diagnosticCodesToIgnore || []).indexOf(d.code) === -1)
|
|
|
|
.filter(d => (this._defaults.getDiagnosticsOptions().diagnosticCodesToIgnore || []).indexOf(d.code) === -1);
|
|
|
|
.map(d => this._convertDiagnostics(model, d));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
monaco.editor.setModelMarkers(model, this._selector, markers);
|
|
|
|
// Fetch lib files if necessary
|
|
|
|
|
|
|
|
const relatedUris = diagnostics
|
|
|
|
|
|
|
|
.map(d => d.relatedInformation || [])
|
|
|
|
|
|
|
|
.reduce((p, c) => c.concat(p), [])
|
|
|
|
|
|
|
|
.map(relatedInformation => relatedInformation.file ? monaco.Uri.parse(relatedInformation.file.fileName) : null);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
await this._libFiles.fetchLibFilesIfNecessary(relatedUris);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (model.isDisposed()) {
|
|
|
|
|
|
|
|
// model was disposed in the meantime
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
monaco.editor.setModelMarkers(model, this._selector, diagnostics.map(d => this._convertDiagnostics(model, d)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private _convertDiagnostics(model: monaco.editor.ITextModel, diag: ts.Diagnostic): monaco.editor.IMarkerData {
|
|
|
|
private _convertDiagnostics(model: monaco.editor.ITextModel, diag: ts.Diagnostic): monaco.editor.IMarkerData {
|
|
|
@ -224,7 +309,7 @@ export class DiagnosticsAdapter extends Adapter {
|
|
|
|
let relatedResource: monaco.editor.ITextModel | null = model;
|
|
|
|
let relatedResource: monaco.editor.ITextModel | null = model;
|
|
|
|
if (info.file) {
|
|
|
|
if (info.file) {
|
|
|
|
const relatedResourceUri = monaco.Uri.parse(info.file.fileName);
|
|
|
|
const relatedResourceUri = monaco.Uri.parse(info.file.fileName);
|
|
|
|
relatedResource = monaco.editor.getModel(relatedResourceUri);
|
|
|
|
relatedResource = this._libFiles.getOrCreateModel(relatedResourceUri);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!relatedResource) {
|
|
|
|
if (!relatedResource) {
|
|
|
@ -479,6 +564,13 @@ export class OccurrencesAdapter extends Adapter implements monaco.languages.Docu
|
|
|
|
|
|
|
|
|
|
|
|
export class DefinitionAdapter extends Adapter {
|
|
|
|
export class DefinitionAdapter extends Adapter {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
|
|
|
private readonly _libFiles: LibFiles,
|
|
|
|
|
|
|
|
worker: (...uris: Uri[]) => Promise<TypeScriptWorker>
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
super(worker);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async provideDefinition(model: monaco.editor.ITextModel, position: Position, token: CancellationToken): Promise<monaco.languages.Definition | undefined> {
|
|
|
|
public async provideDefinition(model: monaco.editor.ITextModel, position: Position, token: CancellationToken): Promise<monaco.languages.Definition | undefined> {
|
|
|
|
const resource = model.uri;
|
|
|
|
const resource = model.uri;
|
|
|
|
const offset = model.getOffsetAt(position);
|
|
|
|
const offset = model.getOffsetAt(position);
|
|
|
@ -489,10 +581,17 @@ export class DefinitionAdapter extends Adapter {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Fetch lib files if necessary
|
|
|
|
|
|
|
|
await this._libFiles.fetchLibFilesIfNecessary(entries.map(entry => Uri.parse(entry.fileName)));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (model.isDisposed()) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const result: monaco.languages.Location[] = [];
|
|
|
|
const result: monaco.languages.Location[] = [];
|
|
|
|
for (let entry of entries) {
|
|
|
|
for (let entry of entries) {
|
|
|
|
const uri = Uri.parse(entry.fileName);
|
|
|
|
const uri = Uri.parse(entry.fileName);
|
|
|
|
const refModel = monaco.editor.getModel(uri);
|
|
|
|
const refModel = this._libFiles.getOrCreateModel(uri);
|
|
|
|
if (refModel) {
|
|
|
|
if (refModel) {
|
|
|
|
result.push({
|
|
|
|
result.push({
|
|
|
|
uri: uri,
|
|
|
|
uri: uri,
|
|
|
@ -508,6 +607,13 @@ export class DefinitionAdapter extends Adapter {
|
|
|
|
|
|
|
|
|
|
|
|
export class ReferenceAdapter extends Adapter implements monaco.languages.ReferenceProvider {
|
|
|
|
export class ReferenceAdapter extends Adapter implements monaco.languages.ReferenceProvider {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
|
|
|
private readonly _libFiles: LibFiles,
|
|
|
|
|
|
|
|
worker: (...uris: Uri[]) => Promise<TypeScriptWorker>
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
super(worker);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async provideReferences(model: monaco.editor.ITextModel, position: Position, context: monaco.languages.ReferenceContext, token: CancellationToken): Promise<monaco.languages.Location[] | undefined> {
|
|
|
|
public async provideReferences(model: monaco.editor.ITextModel, position: Position, context: monaco.languages.ReferenceContext, token: CancellationToken): Promise<monaco.languages.Location[] | undefined> {
|
|
|
|
const resource = model.uri;
|
|
|
|
const resource = model.uri;
|
|
|
|
const offset = model.getOffsetAt(position);
|
|
|
|
const offset = model.getOffsetAt(position);
|
|
|
@ -518,10 +624,17 @@ export class ReferenceAdapter extends Adapter implements monaco.languages.Refere
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Fetch lib files if necessary
|
|
|
|
|
|
|
|
await this._libFiles.fetchLibFilesIfNecessary(entries.map(entry => Uri.parse(entry.fileName)));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (model.isDisposed()) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const result: monaco.languages.Location[] = [];
|
|
|
|
const result: monaco.languages.Location[] = [];
|
|
|
|
for (let entry of entries) {
|
|
|
|
for (let entry of entries) {
|
|
|
|
const uri = Uri.parse(entry.fileName);
|
|
|
|
const uri = Uri.parse(entry.fileName);
|
|
|
|
const refModel = monaco.editor.getModel(uri);
|
|
|
|
const refModel = this._libFiles.getOrCreateModel(uri);
|
|
|
|
if (refModel) {
|
|
|
|
if (refModel) {
|
|
|
|
result.push({
|
|
|
|
result.push({
|
|
|
|
uri: uri,
|
|
|
|
uri: uri,
|
|
|
@ -701,7 +814,7 @@ export class CodeActionAdaptor extends FormatHelper implements monaco.languages.
|
|
|
|
const codeFixes = await worker.getCodeFixesAtPosition(resource.toString(), start, end, errorCodes, formatOptions);
|
|
|
|
const codeFixes = await worker.getCodeFixesAtPosition(resource.toString(), start, end, errorCodes, formatOptions);
|
|
|
|
|
|
|
|
|
|
|
|
if (!codeFixes || model.isDisposed()) {
|
|
|
|
if (!codeFixes || model.isDisposed()) {
|
|
|
|
return { actions: [], dispose:() => {} };
|
|
|
|
return { actions: [], dispose: () => { } };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const actions = codeFixes.filter(fix => {
|
|
|
|
const actions = codeFixes.filter(fix => {
|
|
|
|