Skip to content

Commit 7f84953

Browse files
author
Andy
authored
Merge pull request #10753 from Microsoft/services_modules_2
Break many functions out of services.ts and into their own modules.
2 parents 2ad7162 + c3e63ee commit 7f84953

18 files changed

+6964
-6920
lines changed

Jakefile.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,27 @@ var servicesSources = [
120120
].map(function (f) {
121121
return path.join(compilerDirectory, f);
122122
}).concat([
123+
"types.ts",
124+
"utilities.ts",
123125
"breakpoints.ts",
126+
"classifier.ts",
127+
"completions.ts",
128+
"documentHighlights.ts",
129+
"findAllReferences.ts",
130+
"goToDefinition.ts",
131+
"jsDoc.ts",
132+
"jsTyping.ts",
124133
"navigateTo.ts",
125134
"navigationBar.ts",
126135
"outliningElementsCollector.ts",
127136
"patternMatcher.ts",
137+
"preProcess.ts",
138+
"rename.ts",
128139
"services.ts",
129140
"shims.ts",
130141
"signatureHelp.ts",
131-
"types.ts",
132-
"utilities.ts",
142+
"symbolDisplay.ts",
143+
"transpile.ts",
133144
"formatting/formatting.ts",
134145
"formatting/formattingContext.ts",
135146
"formatting/formattingRequestKind.ts",

src/harness/harnessLanguageService.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,8 @@ namespace Harness.LanguageService {
232232
}
233233
getHost() { return this.host; }
234234
getLanguageService(): ts.LanguageService { return ts.createLanguageService(this.host); }
235-
getClassifier(): ts.Classifier { return ts.createClassifier(); }
236-
getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo { return ts.preProcessFile(fileContents, /* readImportFiles */ true, ts.hasJavaScriptFileExtension(fileName)); }
235+
getClassifier(): ts.Classifier { return ts.Classifier.createClassifier(); }
236+
getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo { return ts.PreProcess.preProcessFile(fileContents, /* readImportFiles */ true, ts.hasJavaScriptFileExtension(fileName)); }
237237
}
238238

239239
/// Shim adapter
@@ -258,7 +258,7 @@ namespace Harness.LanguageService {
258258
};
259259
this.getModuleResolutionsForFile = (fileName) => {
260260
const scriptInfo = this.getScriptInfo(fileName);
261-
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
261+
const preprocessInfo = ts.PreProcess.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
262262
const imports = ts.createMap<string>();
263263
for (const module of preprocessInfo.importedFiles) {
264264
const resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost);
@@ -271,7 +271,7 @@ namespace Harness.LanguageService {
271271
this.getTypeReferenceDirectiveResolutionsForFile = (fileName) => {
272272
const scriptInfo = this.getScriptInfo(fileName);
273273
if (scriptInfo) {
274-
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false);
274+
const preprocessInfo = ts.PreProcess.preProcessFile(scriptInfo.content, /*readImportFiles*/ false);
275275
const resolutions = ts.createMap<ts.ResolvedTypeReferenceDirective>();
276276
const settings = this.nativeHost.getCompilationSettings();
277277
for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) {

src/harness/unittests/services/preProcessFile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
describe("PreProcessFile:", function () {
44
function test(sourceText: string, readImportFile: boolean, detectJavaScriptImports: boolean, expectedPreProcess: ts.PreProcessedFileInfo): void {
5-
const resultPreProcess = ts.preProcessFile(sourceText, readImportFile, detectJavaScriptImports);
5+
const resultPreProcess = ts.PreProcess.preProcessFile(sourceText, readImportFile, detectJavaScriptImports);
66

77
assert.equal(resultPreProcess.isLibFile, expectedPreProcess.isLibFile, "Pre-processed file has different value for isLibFile. Expected: " + expectedPreProcess.isLibFile + ". Actual: " + resultPreProcess.isLibFile);
88

src/server/editorServices.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1566,7 +1566,7 @@ namespace ts.server {
15661566
this.setCompilerOptions(defaultOpts);
15671567
}
15681568
this.languageService = ts.createLanguageService(this.host, this.documentRegistry);
1569-
this.classifier = ts.createClassifier();
1569+
this.classifier = ts.Classifier.createClassifier();
15701570
}
15711571

15721572
setCompilerOptions(opt: ts.CompilerOptions) {

src/services/classifier.ts

Lines changed: 982 additions & 0 deletions
Large diffs are not rendered by default.

src/services/completions.ts

Lines changed: 1647 additions & 0 deletions
Large diffs are not rendered by default.

src/services/documentHighlights.ts

Lines changed: 638 additions & 0 deletions
Large diffs are not rendered by default.

src/services/findAllReferences.ts

Lines changed: 1125 additions & 0 deletions
Large diffs are not rendered by default.

src/services/goToDefinition.ts

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/* @internal */
2+
namespace ts.GoToDefinition {
3+
export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number): DefinitionInfo[] {
4+
/// Triple slash reference comments
5+
const comment = findReferenceInPosition(sourceFile.referencedFiles, position);
6+
if (comment) {
7+
const referenceFile = tryResolveScriptReference(program, sourceFile, comment);
8+
if (referenceFile) {
9+
return [getDefinitionInfoForFileReference(comment.fileName, referenceFile.fileName)];
10+
}
11+
return undefined;
12+
}
13+
14+
// Type reference directives
15+
const typeReferenceDirective = findReferenceInPosition(sourceFile.typeReferenceDirectives, position);
16+
if (typeReferenceDirective) {
17+
const referenceFile = program.getResolvedTypeReferenceDirectives()[typeReferenceDirective.fileName];
18+
if (referenceFile && referenceFile.resolvedFileName) {
19+
return [getDefinitionInfoForFileReference(typeReferenceDirective.fileName, referenceFile.resolvedFileName)];
20+
}
21+
return undefined;
22+
}
23+
24+
const node = getTouchingPropertyName(sourceFile, position);
25+
if (node === sourceFile) {
26+
return undefined;
27+
}
28+
29+
// Labels
30+
if (isJumpStatementTarget(node)) {
31+
const labelName = (<Identifier>node).text;
32+
const label = getTargetLabel((<BreakOrContinueStatement>node.parent), (<Identifier>node).text);
33+
return label ? [createDefinitionInfo(label, ScriptElementKind.label, labelName, /*containerName*/ undefined)] : undefined;
34+
}
35+
36+
const typeChecker = program.getTypeChecker();
37+
38+
const calledDeclaration = tryGetSignatureDeclaration(typeChecker, node);
39+
if (calledDeclaration) {
40+
return [createDefinitionFromSignatureDeclaration(typeChecker, calledDeclaration)];
41+
}
42+
43+
let symbol = typeChecker.getSymbolAtLocation(node);
44+
45+
// Could not find a symbol e.g. node is string or number keyword,
46+
// or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol
47+
if (!symbol) {
48+
return undefined;
49+
}
50+
51+
// If this is an alias, and the request came at the declaration location
52+
// get the aliased symbol instead. This allows for goto def on an import e.g.
53+
// import {A, B} from "mod";
54+
// to jump to the implementation directly.
55+
if (symbol.flags & SymbolFlags.Alias) {
56+
const declaration = symbol.declarations[0];
57+
58+
// Go to the original declaration for cases:
59+
//
60+
// (1) when the aliased symbol was declared in the location(parent).
61+
// (2) when the aliased symbol is originating from a named import.
62+
//
63+
if (node.kind === SyntaxKind.Identifier &&
64+
(node.parent === declaration ||
65+
(declaration.kind === SyntaxKind.ImportSpecifier && declaration.parent && declaration.parent.kind === SyntaxKind.NamedImports))) {
66+
67+
symbol = typeChecker.getAliasedSymbol(symbol);
68+
}
69+
}
70+
71+
// Because name in short-hand property assignment has two different meanings: property name and property value,
72+
// using go-to-definition at such position should go to the variable declaration of the property value rather than
73+
// go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition
74+
// is performed at the location of property access, we would like to go to definition of the property in the short-hand
75+
// assignment. This case and others are handled by the following code.
76+
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
77+
const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(symbol.valueDeclaration);
78+
if (!shorthandSymbol) {
79+
return [];
80+
}
81+
82+
const shorthandDeclarations = shorthandSymbol.getDeclarations();
83+
const shorthandSymbolKind = SymbolDisplay.getSymbolKind(typeChecker, shorthandSymbol, node);
84+
const shorthandSymbolName = typeChecker.symbolToString(shorthandSymbol);
85+
const shorthandContainerName = typeChecker.symbolToString(symbol.parent, node);
86+
return map(shorthandDeclarations,
87+
declaration => createDefinitionInfo(declaration, shorthandSymbolKind, shorthandSymbolName, shorthandContainerName));
88+
}
89+
90+
return getDefinitionFromSymbol(typeChecker, symbol, node);
91+
}
92+
93+
/// Goto type
94+
export function getTypeDefinitionAtPosition(typeChecker: TypeChecker, sourceFile: SourceFile, position: number): DefinitionInfo[] {
95+
const node = getTouchingPropertyName(sourceFile, position);
96+
if (node === sourceFile) {
97+
return undefined;
98+
}
99+
100+
const symbol = typeChecker.getSymbolAtLocation(node);
101+
if (!symbol) {
102+
return undefined;
103+
}
104+
105+
const type = typeChecker.getTypeOfSymbolAtLocation(symbol, node);
106+
if (!type) {
107+
return undefined;
108+
}
109+
110+
if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum)) {
111+
const result: DefinitionInfo[] = [];
112+
forEach((<UnionType>type).types, t => {
113+
if (t.symbol) {
114+
addRange(/*to*/ result, /*from*/ getDefinitionFromSymbol(typeChecker, t.symbol, node));
115+
}
116+
});
117+
return result;
118+
}
119+
120+
if (!type.symbol) {
121+
return undefined;
122+
}
123+
124+
return getDefinitionFromSymbol(typeChecker, type.symbol, node);
125+
}
126+
127+
function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node): DefinitionInfo[] {
128+
const result: DefinitionInfo[] = [];
129+
const declarations = symbol.getDeclarations();
130+
const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, symbol, node);
131+
132+
if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) &&
133+
!tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) {
134+
// Just add all the declarations.
135+
forEach(declarations, declaration => {
136+
result.push(createDefinitionInfo(declaration, symbolKind, symbolName, containerName));
137+
});
138+
}
139+
140+
return result;
141+
142+
function tryAddConstructSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
143+
// Applicable only if we are in a new expression, or we are on a constructor declaration
144+
// and in either case the symbol has a construct signature definition, i.e. class
145+
if (isNewExpressionTarget(location) || location.kind === SyntaxKind.ConstructorKeyword) {
146+
if (symbol.flags & SymbolFlags.Class) {
147+
// Find the first class-like declaration and try to get the construct signature.
148+
for (const declaration of symbol.getDeclarations()) {
149+
if (isClassLike(declaration)) {
150+
return tryAddSignature(declaration.members,
151+
/*selectConstructors*/ true,
152+
symbolKind,
153+
symbolName,
154+
containerName,
155+
result);
156+
}
157+
}
158+
159+
Debug.fail("Expected declaration to have at least one class-like declaration");
160+
}
161+
}
162+
return false;
163+
}
164+
165+
function tryAddCallSignature(symbol: Symbol, location: Node, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
166+
if (isCallExpressionTarget(location) || isNewExpressionTarget(location) || isNameOfFunctionDeclaration(location)) {
167+
return tryAddSignature(symbol.declarations, /*selectConstructors*/ false, symbolKind, symbolName, containerName, result);
168+
}
169+
return false;
170+
}
171+
172+
function tryAddSignature(signatureDeclarations: Declaration[], selectConstructors: boolean, symbolKind: string, symbolName: string, containerName: string, result: DefinitionInfo[]) {
173+
const declarations: Declaration[] = [];
174+
let definition: Declaration;
175+
176+
forEach(signatureDeclarations, d => {
177+
if ((selectConstructors && d.kind === SyntaxKind.Constructor) ||
178+
(!selectConstructors && (d.kind === SyntaxKind.FunctionDeclaration || d.kind === SyntaxKind.MethodDeclaration || d.kind === SyntaxKind.MethodSignature))) {
179+
declarations.push(d);
180+
if ((<FunctionLikeDeclaration>d).body) definition = d;
181+
}
182+
});
183+
184+
if (definition) {
185+
result.push(createDefinitionInfo(definition, symbolKind, symbolName, containerName));
186+
return true;
187+
}
188+
else if (declarations.length) {
189+
result.push(createDefinitionInfo(lastOrUndefined(declarations), symbolKind, symbolName, containerName));
190+
return true;
191+
}
192+
193+
return false;
194+
}
195+
}
196+
197+
function createDefinitionInfo(node: Node, symbolKind: string, symbolName: string, containerName: string): DefinitionInfo {
198+
return {
199+
fileName: node.getSourceFile().fileName,
200+
textSpan: createTextSpanFromBounds(node.getStart(), node.getEnd()),
201+
kind: symbolKind,
202+
name: symbolName,
203+
containerKind: undefined,
204+
containerName
205+
};
206+
}
207+
208+
function getSymbolInfo(typeChecker: TypeChecker, symbol: Symbol, node: Node) {
209+
return {
210+
symbolName: typeChecker.symbolToString(symbol), // Do not get scoped name, just the name of the symbol
211+
symbolKind: SymbolDisplay.getSymbolKind(typeChecker, symbol, node),
212+
containerName: symbol.parent ? typeChecker.symbolToString(symbol.parent, node) : ""
213+
};
214+
}
215+
216+
function createDefinitionFromSignatureDeclaration(typeChecker: TypeChecker, decl: SignatureDeclaration): DefinitionInfo {
217+
const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, decl.symbol, decl);
218+
return createDefinitionInfo(decl, symbolKind, symbolName, containerName);
219+
}
220+
221+
function findReferenceInPosition(refs: FileReference[], pos: number): FileReference {
222+
for (const ref of refs) {
223+
if (ref.pos <= pos && pos < ref.end) {
224+
return ref;
225+
}
226+
}
227+
return undefined;
228+
}
229+
230+
function getDefinitionInfoForFileReference(name: string, targetFileName: string): DefinitionInfo {
231+
return {
232+
fileName: targetFileName,
233+
textSpan: createTextSpanFromBounds(0, 0),
234+
kind: ScriptElementKind.scriptElement,
235+
name: name,
236+
containerName: undefined,
237+
containerKind: undefined
238+
};
239+
}
240+
241+
/** Returns a CallLikeExpression where `node` is the target being invoked. */
242+
function getAncestorCallLikeExpression(node: Node): CallLikeExpression | undefined {
243+
const target = climbPastManyPropertyAccesses(node);
244+
const callLike = target.parent;
245+
return callLike && isCallLikeExpression(callLike) && getInvokedExpression(callLike) === target && callLike;
246+
}
247+
248+
function climbPastManyPropertyAccesses(node: Node): Node {
249+
return isRightSideOfPropertyAccess(node) ? climbPastManyPropertyAccesses(node.parent) : node;
250+
}
251+
252+
function tryGetSignatureDeclaration(typeChecker: TypeChecker, node: Node): SignatureDeclaration | undefined {
253+
const callLike = getAncestorCallLikeExpression(node);
254+
return callLike && typeChecker.getResolvedSignature(callLike).declaration;
255+
}
256+
}

0 commit comments

Comments
 (0)