mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'watchImprovements' into builder
This commit is contained in:
@@ -527,6 +527,10 @@ namespace ts.server {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
getSpanOfEnclosingComment(_fileName: string, _position: number, _onlyMultiLine: boolean): TextSpan {
|
||||
return notImplemented();
|
||||
}
|
||||
|
||||
getCodeFixesAtPosition(file: string, start: number, end: number, errorCodes: number[]): CodeAction[] {
|
||||
const args: protocol.CodeFixRequestArgs = { ...this.createFileRangeRequestArgs(file, start, end), errorCodes };
|
||||
|
||||
|
||||
@@ -563,7 +563,7 @@ namespace ts.server {
|
||||
this.ensureProjectStructuresUptoDate();
|
||||
}
|
||||
const scriptInfo = this.getScriptInfoForNormalizedPath(fileName);
|
||||
return scriptInfo && scriptInfo.getDefaultProject();
|
||||
return scriptInfo && !scriptInfo.isOrphan() && scriptInfo.getDefaultProject();
|
||||
}
|
||||
|
||||
getScriptInfoEnsuringProjectsUptoDate(uncheckedFileName: string) {
|
||||
@@ -592,7 +592,7 @@ namespace ts.server {
|
||||
else {
|
||||
projectsToUpdate = [];
|
||||
for (const f of this.changedFiles) {
|
||||
projectsToUpdate = projectsToUpdate.concat(f.containingProjects);
|
||||
addRange(projectsToUpdate, f.containingProjects);
|
||||
}
|
||||
}
|
||||
this.changedFiles = undefined;
|
||||
@@ -642,7 +642,7 @@ namespace ts.server {
|
||||
private onSourceFileChanged(fileName: NormalizedPath, eventKind: FileWatcherEventKind) {
|
||||
const info = this.getScriptInfoForNormalizedPath(fileName);
|
||||
if (!info) {
|
||||
this.logger.err(`Error: got watch notification for unknown file: ${fileName}`);
|
||||
this.logger.msg(`Error: got watch notification for unknown file: ${fileName}`);
|
||||
}
|
||||
else if (eventKind === FileWatcherEventKind.Deleted) {
|
||||
// File was deleted
|
||||
@@ -793,8 +793,8 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
assignScriptInfoToInferredProject(info: ScriptInfo, projectRootPath?: string) {
|
||||
Debug.assert(info.containingProjects.length === 0);
|
||||
assignOrphanScriptInfoToInferredProject(info: ScriptInfo, projectRootPath?: string) {
|
||||
Debug.assert(info.isOrphan());
|
||||
|
||||
const project = this.getOrCreateInferredProjectForProjectRootPathIfEnabled(info, projectRootPath) ||
|
||||
this.getOrCreateSingleInferredProjectIfEnabled() ||
|
||||
@@ -827,7 +827,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
private addToListOfOpenFiles(info: ScriptInfo) {
|
||||
Debug.assert(info.containingProjects.length !== 0);
|
||||
Debug.assert(!info.isOrphan());
|
||||
for (const p of info.containingProjects) {
|
||||
// file is the part of configured project, addref the project
|
||||
if (p.projectKind === ProjectKind.Configured) {
|
||||
@@ -889,8 +889,8 @@ namespace ts.server {
|
||||
|
||||
// collect orphaned files and assign them to inferred project just like we treat open of a file
|
||||
for (const f of this.openFiles) {
|
||||
if (f.containingProjects.length === 0) {
|
||||
this.assignScriptInfoToInferredProject(f);
|
||||
if (f.isOrphan()) {
|
||||
this.assignOrphanScriptInfoToInferredProject(f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -911,7 +911,7 @@ namespace ts.server {
|
||||
|
||||
private deleteOrphanScriptInfoNotInAnyProject() {
|
||||
this.filenameToScriptInfo.forEach(info => {
|
||||
if (!info.isScriptOpen() && info.containingProjects.length === 0) {
|
||||
if (!info.isScriptOpen() && info.isOrphan()) {
|
||||
// if there are not projects that include this script info - delete it
|
||||
this.stopWatchingScriptInfo(info, WatcherCloseReason.OrphanScriptInfo);
|
||||
this.filenameToScriptInfo.delete(info.path);
|
||||
@@ -1201,27 +1201,27 @@ namespace ts.server {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.group(info => {
|
||||
let counter = 0;
|
||||
counter = printProjects(this.externalProjects, info, counter);
|
||||
counter = printProjects(arrayFrom(this.configuredProjects.values()), info, counter);
|
||||
printProjects(this.inferredProjects, info, counter);
|
||||
|
||||
info("Open files: ");
|
||||
for (const rootFile of this.openFiles) {
|
||||
info(`\t${rootFile.fileName}`);
|
||||
}
|
||||
});
|
||||
|
||||
function printProjects(projects: Project[], info: (msg: string) => void, counter: number): number {
|
||||
this.logger.startGroup();
|
||||
let counter = 0;
|
||||
const printProjects = (projects: Project[], counter: number): number => {
|
||||
for (const project of projects) {
|
||||
info(`Project '${project.getProjectName()}' (${ProjectKind[project.projectKind]}) ${counter}`);
|
||||
info(project.filesToString());
|
||||
info("-----------------------------------------------");
|
||||
this.logger.info(`Project '${project.getProjectName()}' (${ProjectKind[project.projectKind]}) ${counter}`);
|
||||
this.logger.info(project.filesToString());
|
||||
this.logger.info("-----------------------------------------------");
|
||||
counter++;
|
||||
}
|
||||
return counter;
|
||||
};
|
||||
counter = printProjects(this.externalProjects, counter);
|
||||
counter = printProjects(arrayFrom(this.configuredProjects.values()), counter);
|
||||
printProjects(this.inferredProjects, counter);
|
||||
|
||||
this.logger.info("Open files: ");
|
||||
for (const rootFile of this.openFiles) {
|
||||
this.logger.info(`\t${rootFile.fileName}`);
|
||||
}
|
||||
|
||||
this.logger.endGroup();
|
||||
}
|
||||
|
||||
private findConfiguredProjectByProjectName(configFileName: NormalizedPath): ConfiguredProject | undefined {
|
||||
@@ -1813,8 +1813,8 @@ namespace ts.server {
|
||||
|
||||
for (const info of this.openFiles) {
|
||||
// collect all orphaned script infos from open files
|
||||
if (info.containingProjects.length === 0) {
|
||||
this.assignScriptInfoToInferredProject(info);
|
||||
if (info.isOrphan()) {
|
||||
this.assignOrphanScriptInfoToInferredProject(info);
|
||||
}
|
||||
else {
|
||||
// Or remove the root of inferred project if is referenced in more than one projects
|
||||
@@ -1871,8 +1871,8 @@ namespace ts.server {
|
||||
|
||||
// At this point if file is part of any any configured or external project, then it would be present in the containing projects
|
||||
// So if it still doesnt have any containing projects, it needs to be part of inferred project
|
||||
if (info.containingProjects.length === 0) {
|
||||
this.assignScriptInfoToInferredProject(info, projectRootPath);
|
||||
if (info.isOrphan()) {
|
||||
this.assignOrphanScriptInfoToInferredProject(info, projectRootPath);
|
||||
}
|
||||
this.addToListOfOpenFiles(info);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace ts.server.protocol {
|
||||
/* @internal */
|
||||
BraceFull = "brace-full",
|
||||
BraceCompletion = "braceCompletion",
|
||||
GetSpanOfEnclosingComment = "getSpanOfEnclosingComment",
|
||||
Change = "change",
|
||||
Close = "close",
|
||||
Completions = "completions",
|
||||
@@ -241,6 +242,21 @@ namespace ts.server.protocol {
|
||||
body?: TodoComment[];
|
||||
}
|
||||
|
||||
/**
|
||||
* A request to determine if the caret is inside a comment.
|
||||
*/
|
||||
export interface SpanOfEnclosingCommentRequest extends FileLocationRequest {
|
||||
command: CommandTypes.GetSpanOfEnclosingComment;
|
||||
arguments: SpanOfEnclosingCommentRequestArgs;
|
||||
}
|
||||
|
||||
export interface SpanOfEnclosingCommentRequestArgs extends FileLocationRequestArgs {
|
||||
/**
|
||||
* Requires that the enclosing span be a multi-line comment, or else the request returns undefined.
|
||||
*/
|
||||
onlyMultiLine: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to obtain outlining spans in file.
|
||||
*/
|
||||
|
||||
@@ -328,6 +328,10 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
isOrphan() {
|
||||
return this.containingProjects.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param line 1 based index
|
||||
*/
|
||||
|
||||
+15
-13
@@ -140,6 +140,8 @@ namespace ts.server {
|
||||
class Logger implements server.Logger {
|
||||
private fd = -1;
|
||||
private seq = 0;
|
||||
private inGroup = false;
|
||||
private firstInGroup = true;
|
||||
|
||||
constructor(private readonly logFilename: string,
|
||||
private readonly traceToConsole: boolean,
|
||||
@@ -169,24 +171,24 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
perftrc(s: string) {
|
||||
this.msg(s, "Perf");
|
||||
this.msg(s, Msg.Perf);
|
||||
}
|
||||
|
||||
info(s: string) {
|
||||
this.msg(s, "Info");
|
||||
this.msg(s, Msg.Info);
|
||||
}
|
||||
|
||||
err(s: string) {
|
||||
this.msg(s, "Err");
|
||||
this.msg(s, Msg.Err);
|
||||
}
|
||||
|
||||
group(logGroupEntries: (log: (msg: string) => void) => void) {
|
||||
let firstInGroup = false;
|
||||
logGroupEntries(s => {
|
||||
this.msg(s, "Info", /*inGroup*/ true, firstInGroup);
|
||||
firstInGroup = false;
|
||||
});
|
||||
this.seq++;
|
||||
startGroup() {
|
||||
this.inGroup = true;
|
||||
this.firstInGroup = true;
|
||||
}
|
||||
|
||||
endGroup() {
|
||||
this.inGroup = false;
|
||||
}
|
||||
|
||||
loggingEnabled() {
|
||||
@@ -197,16 +199,16 @@ namespace ts.server {
|
||||
return this.loggingEnabled() && this.level >= level;
|
||||
}
|
||||
|
||||
private msg(s: string, type: string, inGroup = false, firstInGroup = false) {
|
||||
msg(s: string, type: Msg.Types = Msg.Err) {
|
||||
if (!this.canWrite) return;
|
||||
|
||||
s = `[${nowString()}] ${s}\n`;
|
||||
if (!inGroup || firstInGroup) {
|
||||
if (!this.inGroup || this.firstInGroup) {
|
||||
const prefix = Logger.padStringRight(type + " " + this.seq.toString(), " ");
|
||||
s = prefix + s;
|
||||
}
|
||||
this.write(s);
|
||||
if (!inGroup) {
|
||||
if (!this.inGroup) {
|
||||
this.seq++;
|
||||
}
|
||||
}
|
||||
|
||||
+19
-11
@@ -368,7 +368,7 @@ namespace ts.server {
|
||||
msg += "\n" + (<StackTraceError>err).stack;
|
||||
}
|
||||
}
|
||||
this.logger.err(msg);
|
||||
this.logger.msg(msg, Msg.Err);
|
||||
}
|
||||
|
||||
public send(msg: protocol.Message) {
|
||||
@@ -616,7 +616,7 @@ namespace ts.server {
|
||||
|
||||
const definitions = project.getLanguageService().getTypeDefinitionAtPosition(file, position);
|
||||
if (!definitions) {
|
||||
return undefined;
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
return definitions.map(def => {
|
||||
@@ -706,7 +706,7 @@ namespace ts.server {
|
||||
const documentHighlights = project.getLanguageService().getDocumentHighlights(file, position, args.filesToSearch);
|
||||
|
||||
if (!documentHighlights) {
|
||||
return undefined;
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
if (simplifiedResult) {
|
||||
@@ -893,7 +893,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | ReadonlyArray<ReferencedSymbol> {
|
||||
private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | undefined | ReadonlyArray<ReferencedSymbol> {
|
||||
const file = toNormalizedPath(args.file);
|
||||
const projects = this.getProjects(args);
|
||||
|
||||
@@ -903,7 +903,7 @@ namespace ts.server {
|
||||
if (simplifiedResult) {
|
||||
const nameInfo = defaultProject.getLanguageService().getQuickInfoAtPosition(file, position);
|
||||
if (!nameInfo) {
|
||||
return emptyArray;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const displayString = displayPartsToString(nameInfo.displayParts);
|
||||
@@ -1015,6 +1015,14 @@ namespace ts.server {
|
||||
return project.getLanguageService(/*ensureSynchronized*/ false).getDocCommentTemplateAtPosition(file, position);
|
||||
}
|
||||
|
||||
private getSpanOfEnclosingComment(args: protocol.SpanOfEnclosingCommentRequestArgs) {
|
||||
const { file, project } = this.getFileAndProjectWithoutRefreshingInferredProjects(args);
|
||||
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
|
||||
const onlyMultiLine = args.onlyMultiLine;
|
||||
const position = this.getPosition(args, scriptInfo);
|
||||
return project.getLanguageService(/*ensureSynchronized*/ false).getSpanOfEnclosingComment(file, position, onlyMultiLine);
|
||||
}
|
||||
|
||||
private getIndentation(args: protocol.IndentationRequestArgs) {
|
||||
const { file, project } = this.getFileAndProjectWithoutRefreshingInferredProjects(args);
|
||||
const position = this.getPosition(args, project.getScriptInfoForNormalizedPath(file));
|
||||
@@ -1157,7 +1165,7 @@ namespace ts.server {
|
||||
});
|
||||
}
|
||||
|
||||
private getCompletions(args: protocol.CompletionsRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.CompletionEntry> | CompletionInfo {
|
||||
private getCompletions(args: protocol.CompletionsRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.CompletionEntry> | CompletionInfo | undefined {
|
||||
const prefix = args.prefix || "";
|
||||
const { file, project } = this.getFileAndProject(args);
|
||||
|
||||
@@ -1165,11 +1173,8 @@ namespace ts.server {
|
||||
const position = this.getPosition(args, scriptInfo);
|
||||
|
||||
const completions = project.getLanguageService().getCompletionsAtPosition(file, position);
|
||||
if (!completions) {
|
||||
return emptyArray;
|
||||
}
|
||||
if (simplifiedResult) {
|
||||
return mapDefined(completions.entries, entry => {
|
||||
return mapDefined(completions && completions.entries, entry => {
|
||||
if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) {
|
||||
const { name, kind, kindModifiers, sortText, replacementSpan } = entry;
|
||||
const convertedSpan = replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined;
|
||||
@@ -1758,6 +1763,9 @@ namespace ts.server {
|
||||
[CommandNames.DocCommentTemplate]: (request: protocol.DocCommentTemplateRequest) => {
|
||||
return this.requiredResponse(this.getDocCommentTemplate(request.arguments));
|
||||
},
|
||||
[CommandNames.GetSpanOfEnclosingComment]: (request: protocol.SpanOfEnclosingCommentRequest) => {
|
||||
return this.requiredResponse(this.getSpanOfEnclosingComment(request.arguments));
|
||||
},
|
||||
[CommandNames.Format]: (request: protocol.FormatRequest) => {
|
||||
return this.requiredResponse(this.getFormattingEditsForRange(request.arguments));
|
||||
},
|
||||
@@ -1940,7 +1948,7 @@ namespace ts.server {
|
||||
return this.executeWithRequestId(request.seq, () => handler(request));
|
||||
}
|
||||
else {
|
||||
this.logger.err(`Unrecognized JSON command: ${JSON.stringify(request)}`);
|
||||
this.logger.msg(`Unrecognized JSON command: ${JSON.stringify(request)}`, Msg.Err);
|
||||
this.output(undefined, CommandNames.Unknown, request.seq, `Unrecognized JSON command: ${request.command}`);
|
||||
return { responseRequired: false };
|
||||
}
|
||||
|
||||
+13
-4
@@ -17,11 +17,22 @@ namespace ts.server {
|
||||
loggingEnabled(): boolean;
|
||||
perftrc(s: string): void;
|
||||
info(s: string): void;
|
||||
err(s: string): void;
|
||||
group(logGroupEntries: (log: (msg: string) => void) => void): void;
|
||||
startGroup(): void;
|
||||
endGroup(): void;
|
||||
msg(s: string, type?: Msg.Types): void;
|
||||
getLogFileName(): string;
|
||||
}
|
||||
|
||||
export namespace Msg {
|
||||
export type Err = "Err";
|
||||
export const Err: Err = "Err";
|
||||
export type Info = "Info";
|
||||
export const Info: Info = "Info";
|
||||
export type Perf = "Perf";
|
||||
export const Perf: Perf = "Perf";
|
||||
export type Types = Err | Info | Perf;
|
||||
}
|
||||
|
||||
function getProjectRootPath(project: Project): Path {
|
||||
switch (project.projectKind) {
|
||||
case ProjectKind.Configured:
|
||||
@@ -115,9 +126,7 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
export function createNormalizedPathMap<T>(): NormalizedPathMap<T> {
|
||||
/* tslint:disable:no-null-keyword */
|
||||
const map = createMap<T>();
|
||||
/* tslint:enable:no-null-keyword */
|
||||
return {
|
||||
get(path) {
|
||||
return map.get(path);
|
||||
|
||||
Reference in New Issue
Block a user