Make TypeScript extension ready for 2.0

This commit is contained in:
Dirk Baeumer
2016-08-17 12:42:58 +02:00
parent 79360dbed9
commit c4d6481247
8 changed files with 204 additions and 43 deletions

View File

@@ -4,15 +4,24 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as path from 'path';
import { workspace, TextDocument, TextDocumentChangeEvent, TextDocumentContentChangeEvent, Disposable } from 'vscode';
import * as Proto from '../protocol';
import { ITypescriptServiceClient } from '../typescriptService';
import { ITypescriptServiceClient, APIVersion } from '../typescriptService';
import { Delayer } from '../utils/async';
interface IDiagnosticRequestor {
requestDiagnostic(filepath: string): void;
}
const Mode2ScriptKind: Map<"TS" | "JS" | "TSX" | "JSX"> = {
'typescript': 'TS',
'typescriptreact': 'TSX',
'javascript': 'JS',
'javascriptreact': 'JSX'
};
class SyncedBuffer {
private document: TextDocument;
@@ -30,8 +39,19 @@ class SyncedBuffer {
public open(): void {
let args: Proto.OpenRequestArgs = {
file: this.filepath,
fileContent: this.document.getText()
fileContent: this.document.getText(),
};
if (this.client.apiVersion === APIVersion.v2_0_0) {
// we have no extension. So check the mode and
// set the script kind accordningly.
const ext = path.extname(this.filepath);
if (ext === '') {
const scriptKind = Mode2ScriptKind[this.document.languageId];
if (scriptKind) {
args.scriptKindName = scriptKind;
}
}
}
this.client.execute('open', args, false);
}

View File

@@ -8,7 +8,7 @@
import { ReferenceProvider, Location, TextDocument, Position, Range, CancellationToken } from 'vscode';
import * as Proto from '../protocol';
import { ITypescriptServiceClient } from '../typescriptService';
import { ITypescriptServiceClient, APIVersion } from '../typescriptService';
export default class TypeScriptReferenceSupport implements ReferenceProvider {
@@ -29,11 +29,15 @@ export default class TypeScriptReferenceSupport implements ReferenceProvider {
if (!args.file) {
return Promise.resolve<Location[]>([]);
}
const apiVersion = this.client.apiVersion;
return this.client.execute('references', args, token).then((msg) => {
let result: Location[] = [];
let refs = msg.body.refs;
for (let i = 0; i < refs.length; i++) {
let ref = refs[i];
if (!options.includeDeclaration && apiVersion >= APIVersion.v2_0_0 && ref.isDefinition) {
continue;
}
let url = this.client.asUrl(ref.file);
let location = new Location(
url,

View File

@@ -124,6 +124,10 @@ export interface ProjectInfo {
* The list of normalized file name in the project, including 'lib.d.ts'
*/
fileNames?: string[];
/**
* Indicates if the project has a active language service instance
*/
languageServiceDisabled?: boolean;
}
/**
@@ -305,6 +309,11 @@ export interface ReferencesResponseItem extends FileSpan {
* True if reference is a write location, false otherwise.
*/
isWriteAccess: boolean;
/**
* True if reference is a definition, false otherwise.
*/
isDefinition: boolean;
}
/**
@@ -430,6 +439,9 @@ export interface EditorOptions {
/** Number of spaces to indent during formatting. Default value is 4. */
indentSize?: number;
/** Number of additional spaces to indent during formatting to preserve base indentation (ex. script block indentation). Default value is 0. */
baseIndentSize?: number;
/** The new line character to be used. Default value is the OS line delimiter. */
newLineCharacter?: string;
@@ -445,7 +457,7 @@ export interface FormatOptions extends EditorOptions {
/** Defines space handling after a comma delimiter. Default value is true. */
insertSpaceAfterCommaDelimiter?: boolean;
/** Defines space handling after a semicolon in a for statemen. Default value is true */
/** Defines space handling after a semicolon in a for statement. Default value is true */
insertSpaceAfterSemicolonInForStatements?: boolean;
/** Defines space handling after a binary operator. Default value is true. */
@@ -532,6 +544,11 @@ export interface OpenRequestArgs extends FileRequestArgs {
* Then the known content will be used upon opening instead of the disk copy
*/
fileContent?: string;
/**
* Used to specify the script kind of the file explicitly. It could be one of the following:
* "TS", "JS", "TSX", "JSX"
*/
scriptKindName?: "TS" | "JS" | "TSX" | "JSX";
}
/**
@@ -849,7 +866,7 @@ export interface SignatureHelpItem {
prefixDisplayParts: SymbolDisplayPart[];
/**
* The suffix disaply parts.
* The suffix display parts.
*/
suffixDisplayParts: SymbolDisplayPart[];
@@ -904,7 +921,6 @@ export interface SignatureHelpItems {
* Arguments of a signature help request.
*/
export interface SignatureHelpRequestArgs extends FileLocationRequestArgs {
}
/**
@@ -917,12 +933,38 @@ export interface SignatureHelpRequest extends FileLocationRequest {
}
/**
* Repsonse object for a SignatureHelpRequest.
* Response object for a SignatureHelpRequest.
*/
export interface SignatureHelpResponse extends Response {
body?: SignatureHelpItems;
}
/**
* Synchronous request for semantic diagnostics of one file.
*/
export interface SemanticDiagnosticsSyncRequest extends FileRequest {
}
/**
* Response object for synchronous sematic diagnostics request.
*/
export interface SemanticDiagnosticsSyncResponse extends Response {
body?: Diagnostic[];
}
/**
* Synchronous request for syntactic diagnostics of one file.
*/
export interface SyntacticDiagnosticsSyncRequest extends FileRequest {
}
/**
* Response object for synchronous syntactic diagnostics request.
*/
export interface SyntacticDiagnosticsSyncResponse extends Response {
body?: Diagnostic[];
}
/**
* Arguments for GeterrForProject request.
*/
@@ -984,7 +1026,7 @@ export interface GeterrRequest extends Request {
*/
export interface Diagnostic {
/**
* Starting file location at which text appies.
* Starting file location at which text applies.
*/
start: Location;
@@ -1024,6 +1066,32 @@ export interface DiagnosticEvent extends Event {
body?: DiagnosticEventBody;
}
export interface ConfigFileDiagnosticEventBody {
/**
* The file which trigged the searching and error-checking of the config file
*/
triggerFile: string;
/**
* The name of the found config file.
*/
configFile: string;
/**
* An arry of diagnostic information items for the found config file.
*/
diagnostics: Diagnostic[];
}
/**
* Event message for "configFileDiag" event type.
* This event provides errors for a found config file.
*/
export interface ConfigFileDiagnosticEvent extends Event {
body?: ConfigFileDiagnosticEventBody;
event: "configFileDiag";
}
/**
* Arguments for reload request.
*/
@@ -1198,7 +1266,7 @@ export interface BraceRequest extends FileLocationRequest {
}
/**
* NavBar itesm request; value of command field is "navbar".
* NavBar items request; value of command field is "navbar".
* Return response giving the list of navigation bar entries
* extracted from the requested file.
*/
@@ -1230,8 +1298,13 @@ export interface NavigationBarItem {
* Optional children.
*/
childItems?: NavigationBarItem[];
/**
* Number of levels deep this item should appear.
*/
indent: number;
}
export interface NavBarResponse extends Response {
body?: NavigationBarItem[];
}
}

View File

@@ -262,6 +262,10 @@ class LanguageProvider {
}
this.currentDiagnostics.set(Uri.file(file), diagnostics);
}
public configFileDiagnosticsReceived(file: string, diagnostics: Diagnostic[]): void {
this.currentDiagnostics.set(Uri.file(file), diagnostics);
}
}
class TypeScriptServiceClientHost implements ITypescriptServiceClientHost {
@@ -351,6 +355,18 @@ class TypeScriptServiceClientHost implements ITypescriptServiceClientHost {
}
}
/* internal */ configFileDiagnosticsReceived(event: Proto.ConfigFileDiagnosticEvent): void {
/* See https://github.com/Microsoft/TypeScript/issues/10384
const body = event.body;
if (body.diagnostics) {
const language = this.findLanguage(body.triggerFile);
if (language) {
language.configFileDiagnosticsReceived(body.configFile, this.createMarkerDatas(body.diagnostics, language.diagnosticSource));
}
}
*/
}
private createMarkerDatas(diagnostics: Proto.Diagnostic[], source: string): Diagnostic[] {
let result: Diagnostic[] = [];
for (let diagnostic of diagnostics) {

View File

@@ -11,9 +11,37 @@ import * as Proto from './protocol';
export interface ITypescriptServiceClientHost {
syntaxDiagnosticsReceived(event: Proto.DiagnosticEvent): void;
semanticDiagnosticsReceived(event: Proto.DiagnosticEvent): void;
configFileDiagnosticsReceived(event: Proto.ConfigFileDiagnosticEvent): void;
populateService(): void;
}
export enum APIVersion {
v1_x = 1,
v2_0_0 = 2
};
export namespace APIVersion {
export function fromString(value: string): APIVersion {
if (!value) {
return APIVersion.v1_x;
}
const index = value.indexOf('.');
var major: number;
if (index > 0) {
major = parseInt(value.substr(0, index));
} else {
major = parseInt(value);
}
if (isNaN(major)) {
return APIVersion.v1_x;
}
if (major >= 2) {
return APIVersion.v2_0_0;
}
return APIVersion.v1_x;
}
}
export interface ITypescriptServiceClient {
asAbsolutePath(resource: Uri): string;
asUrl(filepath: string): Uri;
@@ -21,26 +49,27 @@ export interface ITypescriptServiceClient {
logTelemetry(eventName: string, properties?: { [prop: string]: string });
experimentalAutoBuild: boolean;
apiVersion: APIVersion;
execute(command:'configure', args: Proto.ConfigureRequestArguments, token?: CancellationToken):Promise<Proto.ConfigureResponse>;
execute(command:'open', args: Proto.OpenRequestArgs, expectedResult:boolean, token?: CancellationToken):Promise<any>;
execute(command:'close', args: Proto.FileRequestArgs, expectedResult:boolean, token?: CancellationToken):Promise<any>;
execute(command:'change', args: Proto.ChangeRequestArgs, expectedResult:boolean, token?: CancellationToken):Promise<any>;
execute(command:'geterr', args: Proto.GeterrRequestArgs, expectedResult:boolean, token?: CancellationToken):Promise<any>;
execute(command:'quickinfo', args: Proto.FileLocationRequestArgs, token?: CancellationToken):Promise<Proto.QuickInfoResponse>;
execute(command:'completions', args: Proto.CompletionsRequestArgs, token?: CancellationToken):Promise<Proto.CompletionsResponse>;
execute(commant:'completionEntryDetails', args: Proto.CompletionDetailsRequestArgs, token?: CancellationToken):Promise<Proto.CompletionDetailsResponse>;
execute(commant:'signatureHelp', args: Proto.SignatureHelpRequestArgs, token?: CancellationToken):Promise<Proto.SignatureHelpResponse>;
execute(command:'definition', args: Proto.FileLocationRequestArgs, token?: CancellationToken):Promise<Proto.DefinitionResponse>;
execute(command:'references', args: Proto.FileLocationRequestArgs, token?: CancellationToken):Promise<Proto.ReferencesResponse>;
execute(command:'navto', args: Proto.NavtoRequestArgs, token?: CancellationToken):Promise<Proto.NavtoResponse>;
execute(command:'navbar', args: Proto.FileRequestArgs, token?: CancellationToken):Promise<Proto.NavBarResponse>;
execute(command:'format', args: Proto.FormatRequestArgs, token?: CancellationToken):Promise<Proto.FormatResponse>;
execute(command:'formatonkey', args: Proto.FormatOnKeyRequestArgs, token?: CancellationToken):Promise<Proto.FormatResponse>;
execute(command:'rename', args: Proto.RenameRequestArgs, token?: CancellationToken): Promise<Proto.RenameResponse>;
execute(command:'occurrences', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise<Proto.OccurrencesResponse>;
execute(command:'projectInfo', args: Proto.ProjectInfoRequestArgs, token?: CancellationToken): Promise<Proto.ProjectInfoResponse>;
execute(command:'reloadProjects', args: any, expectedResult:boolean, token?: CancellationToken): Promise<any>;
execute(command:'reload', args: Proto.ReloadRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise<any>;
execute(command:string, args:any, expectedResult:boolean| CancellationToken, token?: CancellationToken):Promise<any>;
execute(command: 'configure', args: Proto.ConfigureRequestArguments, token?: CancellationToken):Promise<Proto.ConfigureResponse>;
execute(command: 'open', args: Proto.OpenRequestArgs, expectedResult:boolean, token?: CancellationToken):Promise<any>;
execute(command: 'close', args: Proto.FileRequestArgs, expectedResult:boolean, token?: CancellationToken):Promise<any>;
execute(command: 'change', args: Proto.ChangeRequestArgs, expectedResult:boolean, token?: CancellationToken):Promise<any>;
execute(command: 'geterr', args: Proto.GeterrRequestArgs, expectedResult:boolean, token?: CancellationToken):Promise<any>;
execute(command: 'quickinfo', args: Proto.FileLocationRequestArgs, token?: CancellationToken):Promise<Proto.QuickInfoResponse>;
execute(command: 'completions', args: Proto.CompletionsRequestArgs, token?: CancellationToken):Promise<Proto.CompletionsResponse>;
execute(commant: 'completionEntryDetails', args: Proto.CompletionDetailsRequestArgs, token?: CancellationToken):Promise<Proto.CompletionDetailsResponse>;
execute(commant: 'signatureHelp', args: Proto.SignatureHelpRequestArgs, token?: CancellationToken):Promise<Proto.SignatureHelpResponse>;
execute(command: 'definition', args: Proto.FileLocationRequestArgs, token?: CancellationToken):Promise<Proto.DefinitionResponse>;
execute(command: 'references', args: Proto.FileLocationRequestArgs, token?: CancellationToken):Promise<Proto.ReferencesResponse>;
execute(command: 'navto', args: Proto.NavtoRequestArgs, token?: CancellationToken):Promise<Proto.NavtoResponse>;
execute(command: 'navbar', args: Proto.FileRequestArgs, token?: CancellationToken):Promise<Proto.NavBarResponse>;
execute(command: 'format', args: Proto.FormatRequestArgs, token?: CancellationToken):Promise<Proto.FormatResponse>;
execute(command: 'formatonkey', args: Proto.FormatOnKeyRequestArgs, token?: CancellationToken):Promise<Proto.FormatResponse>;
execute(command: 'rename', args: Proto.RenameRequestArgs, token?: CancellationToken): Promise<Proto.RenameResponse>;
execute(command: 'occurrences', args: Proto.FileLocationRequestArgs, token?: CancellationToken): Promise<Proto.OccurrencesResponse>;
execute(command: 'projectInfo', args: Proto.ProjectInfoRequestArgs, token?: CancellationToken): Promise<Proto.ProjectInfoResponse>;
execute(command: 'reloadProjects', args: any, expectedResult:boolean, token?: CancellationToken): Promise<any>;
execute(command: 'reload', args: Proto.ReloadRequestArgs, expectedResult: boolean, token?: CancellationToken): Promise<any>;
execute(command: string, args: any, expectedResult: boolean | CancellationToken, token?: CancellationToken): Promise<any>;
}

View File

@@ -14,7 +14,7 @@ import { Reader } from './utils/wireProtocol';
import { workspace, window, Uri, CancellationToken, OutputChannel } from 'vscode';
import * as Proto from './protocol';
import { ITypescriptServiceClient, ITypescriptServiceClientHost } from './typescriptService';
import { ITypescriptServiceClient, ITypescriptServiceClientHost, APIVersion } from './typescriptService';
import * as VersionStatus from './utils/versionStatus';
@@ -90,6 +90,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
private callbacks: CallbackMap;
private _packageInfo: IPackageInfo;
private _apiVersion: APIVersion;
private telemetryReporter: TelemetryReporter;
constructor(host: ITypescriptServiceClientHost, storagePath: string) {
@@ -115,6 +116,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
const configuration = workspace.getConfiguration();
this.tsdk = configuration.get<string>('typescript.tsdk', null);
this._experimentalAutoBuild = configuration.get<boolean>('typescript.tsserver.experimentalAutoBuild', false);
this._apiVersion = APIVersion.v1_x;
this.trace = this.readTrace();
workspace.onDidChangeConfiguration(() => {
this.trace = this.readTrace();
@@ -145,6 +147,10 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
return this._experimentalAutoBuild;
}
public get apiVersion(): APIVersion {
return this._apiVersion;
}
public onReady(): Promise<void> {
return this._onReady.promise;
}
@@ -202,8 +208,15 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
return;
}
let label = this.getTypeScriptVersion(modulePath);
let tooltip = modulePath;
let version = this.getTypeScriptVersion(modulePath);
if (!version) {
version = workspace.getConfiguration().get<string>('typescript.tsdk_version', undefined);
}
if (version) {
this._apiVersion = APIVersion.fromString(version);
}
const label = version || localize('versionNumber.custom' ,'custom');
const tooltip = modulePath;
VersionStatus.enable(!!this.tsdk);
VersionStatus.setInfo(label, tooltip);
@@ -264,26 +277,25 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
}
private getTypeScriptVersion(serverPath: string): string {
const custom = localize('versionNumber.custom' ,'custom');
let p = serverPath.split(path.sep);
if (p.length <= 2) {
return custom;
return undefined;
}
let p2 = p.slice(0, -2);
let modulePath = p2.join(path.sep);
let fileName = path.join(modulePath, 'package.json');
if (!fs.existsSync(fileName)) {
return custom;
return undefined;
}
let contents = fs.readFileSync(fileName).toString();
let desc = null;
try {
desc = JSON.parse(contents);
} catch(err) {
return custom;
return undefined;
}
if (!desc.version) {
return custom;
return undefined;
}
return desc.version;
}
@@ -431,10 +443,11 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
let event: Proto.Event = <Proto.Event>message;
this.traceEvent(event);
if (event.event === 'syntaxDiag') {
this.host.syntaxDiagnosticsReceived(event);
}
if (event.event === 'semanticDiag') {
this.host.semanticDiagnosticsReceived(event);
this.host.syntaxDiagnosticsReceived(event as Proto.DiagnosticEvent);
} else if (event.event === 'semanticDiag') {
this.host.semanticDiagnosticsReceived(event as Proto.DiagnosticEvent);
} else if (event.event === 'configFileDiag') {
this.host.configFileDiagnosticsReceived(event as Proto.ConfigFileDiagnosticEvent);
}
} else {
throw new Error('Unknown message type ' + message.type + ' recevied');