diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 9f675e3ffd0..d14dfc8f80f 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -202,7 +202,7 @@ namespace FourSlash { // Whether or not we should format on keystrokes public enableFormatting = true; - public formatCodeOptions: ts.FormatCodeOptions; + public formatCodeSettings: ts.FormatCodeSettings; private inputFiles: ts.Map = {}; // Map between inputFile's fileName and its content for easily looking up when resolving references @@ -309,22 +309,22 @@ namespace FourSlash { Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false); } - this.formatCodeOptions = { - IndentSize: 4, - TabSize: 4, - NewLineCharacter: Harness.IO.newLine(), - ConvertTabsToSpaces: true, - IndentStyle: ts.IndentStyle.Smart, - InsertSpaceAfterCommaDelimiter: true, - InsertSpaceAfterSemicolonInForStatements: true, - InsertSpaceBeforeAndAfterBinaryOperators: true, - InsertSpaceAfterKeywordsInControlFlowStatements: true, - InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false, - InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, - InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, - InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, - PlaceOpenBraceOnNewLineForFunctions: false, - PlaceOpenBraceOnNewLineForControlBlocks: false, + this.formatCodeSettings = { + indentSize: 4, + tabSize: 4, + newLineCharacter: Harness.IO.newLine(), + convertTabsToSpaces: true, + indentStyle: ts.IndentStyle.Smart, + insertSpaceAfterCommaDelimiter: true, + insertSpaceAfterSemicolonInForStatements: true, + insertSpaceBeforeAndAfterBinaryOperators: true, + insertSpaceAfterKeywordsInControlFlowStatements: true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, + placeOpenBraceOnNewLineForFunctions: false, + placeOpenBraceOnNewLineForControlBlocks: false, }; // Open the first file by default @@ -1278,7 +1278,7 @@ namespace FourSlash { // Handle post-keystroke formatting if (this.enableFormatting) { - const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeOptions); + const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings); if (edits.length) { offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); // this.checkPostEditInvariants(); @@ -1316,7 +1316,7 @@ namespace FourSlash { // Handle post-keystroke formatting if (this.enableFormatting) { - const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeOptions); + const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings); if (edits.length) { offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); } @@ -1369,7 +1369,7 @@ namespace FourSlash { // Handle post-keystroke formatting if (this.enableFormatting) { - const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeOptions); + const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeSettings); if (edits.length) { offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); // this.checkPostEditInvariants(); @@ -1395,7 +1395,7 @@ namespace FourSlash { // Handle formatting if (this.enableFormatting) { - const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, offset, this.formatCodeOptions); + const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, offset, this.formatCodeSettings); if (edits.length) { offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); this.checkPostEditInvariants(); @@ -1469,30 +1469,30 @@ namespace FourSlash { return runningOffset; } - public copyFormatOptions(): ts.FormatCodeOptions { - return ts.clone(this.formatCodeOptions); + public copyFormatOptions(): ts.FormatCodeSettings { + return ts.clone(this.formatCodeSettings); } - public setFormatOptions(formatCodeOptions: ts.FormatCodeOptions): ts.FormatCodeOptions { - const oldFormatCodeOptions = this.formatCodeOptions; - this.formatCodeOptions = formatCodeOptions; + public setFormatOptions(formatCodeOptions: ts.FormatCodeOptions | ts.FormatCodeSettings): ts.FormatCodeSettings { + const oldFormatCodeOptions = this.formatCodeSettings; + this.formatCodeSettings = ts.toEditorSettings(formatCodeOptions); return oldFormatCodeOptions; } public formatDocument() { - const edits = this.languageService.getFormattingEditsForDocument(this.activeFile.fileName, this.formatCodeOptions); + const edits = this.languageService.getFormattingEditsForDocument(this.activeFile.fileName, this.formatCodeSettings); this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); this.fixCaretPosition(); } public formatSelection(start: number, end: number) { - const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, end, this.formatCodeOptions); + const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, end, this.formatCodeSettings); this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); this.fixCaretPosition(); } public formatOnType(pos: number, key: string) { - const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeOptions); + const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeSettings); this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); this.fixCaretPosition(); } @@ -1621,8 +1621,8 @@ namespace FourSlash { private getIndentation(fileName: string, position: number, indentStyle: ts.IndentStyle): number { - const formatOptions = ts.clone(this.formatCodeOptions); - formatOptions.IndentStyle = indentStyle; + const formatOptions = ts.clone(this.formatCodeSettings); + formatOptions.indentStyle = indentStyle; return this.languageService.getIndentationAtPosition(fileName, position, formatOptions); } @@ -3226,7 +3226,7 @@ namespace FourSlashInterface { this.state.formatDocument(); } - public copyFormatOptions(): ts.FormatCodeOptions { + public copyFormatOptions(): ts.FormatCodeSettings { return this.state.copyFormatOptions(); } @@ -3246,7 +3246,7 @@ namespace FourSlashInterface { public setOption(name: string, value: string): void; public setOption(name: string, value: boolean): void; public setOption(name: string, value: any): void { - this.state.formatCodeOptions[name] = value; + (this.state.formatCodeSettings)[name] = value; } } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index f77abe5e25f..bfbee344b7a 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -16,53 +16,51 @@ namespace ts.server { msg(s: string, type?: string): void; } - function getDefaultFormatCodeOptions(host: ServerHost): ts.FormatCodeOptions { - return ts.clone({ - IndentSize: 4, - TabSize: 4, - NewLineCharacter: host.newLine || "\n", - ConvertTabsToSpaces: true, - IndentStyle: ts.IndentStyle.Smart, - InsertSpaceAfterCommaDelimiter: true, - InsertSpaceAfterSemicolonInForStatements: true, - InsertSpaceBeforeAndAfterBinaryOperators: true, - InsertSpaceAfterKeywordsInControlFlowStatements: true, - InsertSpaceAfterFunctionKeywordForAnonymousFunctions: false, - InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, - InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, - InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, - PlaceOpenBraceOnNewLineForFunctions: false, - PlaceOpenBraceOnNewLineForControlBlocks: false, + function getDefaultFormatCodeSettings(host: ServerHost): ts.FormatCodeSettings { + return ts.clone({ + indentSize: 4, + tabSize: 4, + newLineCharacter: host.newLine || "\n", + convertTabsToSpaces: true, + indentStyle: ts.IndentStyle.Smart, + insertSpaceAfterCommaDelimiter: true, + insertSpaceAfterSemicolonInForStatements: true, + insertSpaceBeforeAndAfterBinaryOperators: true, + insertSpaceAfterKeywordsInControlFlowStatements: true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, + placeOpenBraceOnNewLineForFunctions: false, + placeOpenBraceOnNewLineForControlBlocks: false, }); } - function mergeFormatOptions(formatCodeOptions: FormatCodeOptions, formatOptions: protocol.FormatOptions): void { - const hasOwnProperty = Object.prototype.hasOwnProperty; - Object.keys(formatOptions).forEach((key) => { - const codeKey = key.charAt(0).toUpperCase() + key.substring(1); - if (hasOwnProperty.call(formatCodeOptions, codeKey)) { - formatCodeOptions[codeKey] = formatOptions[key]; + function mergeMaps(target: Map, source: Map): void { + for (const key in source) { + if (hasProperty(source, key)) { + target[key] = source[key]; } - }); + } } export class ScriptInfo { svc: ScriptVersionCache; defaultProject: Project; // project to use by default for file fileWatcher: FileWatcher; - formatCodeOptions: ts.FormatCodeOptions; + formatCodeSettings: ts.FormatCodeSettings; path: Path; scriptKind: ScriptKind; constructor(private host: ServerHost, public fileName: string, public content: string, public isOpen = false) { this.path = toPath(fileName, host.getCurrentDirectory(), createGetCanonicalFileName(host.useCaseSensitiveFileNames)); this.svc = ScriptVersionCache.fromString(host, content); - this.formatCodeOptions = getDefaultFormatCodeOptions(this.host); + this.formatCodeSettings = getDefaultFormatCodeSettings(this.host); } - setFormatOptions(formatOptions: protocol.FormatOptions): void { - if (formatOptions) { - mergeFormatOptions(this.formatCodeOptions, formatOptions); + setFormatOptions(formatSettings: protocol.FormatOptions): void { + if (formatSettings) { + mergeMaps(this.formatCodeSettings, formatSettings); } } @@ -603,7 +601,7 @@ namespace ts.server { } export interface HostConfiguration { - formatCodeOptions: ts.FormatCodeOptions; + formatCodeOptions: ts.FormatCodeSettings; hostInfo: string; } @@ -665,7 +663,7 @@ namespace ts.server { private setDefaultHostConfiguration() { this.hostConfiguration = { - formatCodeOptions: getDefaultFormatCodeOptions(this.host), + formatCodeOptions: getDefaultFormatCodeSettings(this.host), hostInfo: "Unknown host" }; } @@ -693,7 +691,7 @@ namespace ts.server { if (file) { const info = this.filenameToScriptInfo[file]; if (info) { - return info.formatCodeOptions; + return info.formatCodeSettings; } } return this.hostConfiguration.formatCodeOptions; @@ -1286,7 +1284,7 @@ namespace ts.server { if (content !== undefined) { info = new ScriptInfo(this.host, fileName, content, openedByClient); info.scriptKind = scriptKind; - info.setFormatOptions(this.getFormatCodeOptions()); + info.setFormatOptions(toEditorSettings(this.getFormatCodeOptions())); this.filenameToScriptInfo[fileName] = info; if (!info.isOpen) { info.fileWatcher = this.host.watchFile(fileName, _ => { this.onSourceFileChanged(fileName); }); @@ -1322,7 +1320,7 @@ namespace ts.server { this.log("Host information " + args.hostInfo, "Info"); } if (args.formatOptions) { - mergeFormatOptions(this.hostConfiguration.formatCodeOptions, args.formatOptions); + mergeMaps(this.hostConfiguration.formatCodeOptions, args.formatOptions); this.log("Format host information updated", "Info"); } } diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts index 5fddf266ffd..1d77aeba2ac 100644 --- a/src/server/protocol.d.ts +++ b/src/server/protocol.d.ts @@ -544,9 +544,6 @@ declare namespace ts.server.protocol { /** Defines whether an open brace is put onto a new line for control blocks or not. Default value is false. */ placeOpenBraceOnNewLineForControlBlocks?: boolean; - - /** Index operator */ - [key: string]: string | number | boolean; } /** diff --git a/src/server/session.ts b/src/server/session.ts index 11c413a7a18..03fee122d39 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -778,10 +778,10 @@ namespace ts.server { if (lineText.search("\\S") < 0) { // TODO: get these options from host const editorOptions: ts.EditorOptions = { - IndentSize: formatOptions.IndentSize, - TabSize: formatOptions.TabSize, - NewLineCharacter: formatOptions.NewLineCharacter, - ConvertTabsToSpaces: formatOptions.ConvertTabsToSpaces, + IndentSize: formatOptions.indentSize, + TabSize: formatOptions.tabSize, + NewLineCharacter: formatOptions.newLineCharacter, + ConvertTabsToSpaces: formatOptions.convertTabsToSpaces, IndentStyle: ts.IndentStyle.Smart, }; const preferredIndent = project.languageService.getIndentationAtPosition(file, position, editorOptions); diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index ef8fddcfb3a..fccb9fb0b1c 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -67,7 +67,7 @@ namespace ts.formatting { delta: number; } - export function formatOnEnter(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] { + export function formatOnEnter(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] { const line = sourceFile.getLineAndCharacterOfPosition(position).line; if (line === 0) { return []; @@ -96,15 +96,15 @@ namespace ts.formatting { return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnEnter); } - export function formatOnSemicolon(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] { + export function formatOnSemicolon(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] { return formatOutermostParent(position, SyntaxKind.SemicolonToken, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnSemicolon); } - export function formatOnClosingCurly(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] { + export function formatOnClosingCurly(position: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] { return formatOutermostParent(position, SyntaxKind.CloseBraceToken, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnClosingCurlyBrace); } - export function formatDocument(sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] { + export function formatDocument(sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] { const span = { pos: 0, end: sourceFile.text.length @@ -112,7 +112,7 @@ namespace ts.formatting { return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatDocument); } - export function formatSelection(start: number, end: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeOptions): TextChange[] { + export function formatSelection(start: number, end: number, sourceFile: SourceFile, rulesProvider: RulesProvider, options: FormatCodeSettings): TextChange[] { // format from the beginning of the line const span = { pos: getLineStartPositionForPosition(start, sourceFile), @@ -121,7 +121,7 @@ namespace ts.formatting { return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatSelection); } - function formatOutermostParent(position: number, expectedLastToken: SyntaxKind, sourceFile: SourceFile, options: FormatCodeOptions, rulesProvider: RulesProvider, requestKind: FormattingRequestKind): TextChange[] { + function formatOutermostParent(position: number, expectedLastToken: SyntaxKind, sourceFile: SourceFile, options: FormatCodeSettings, rulesProvider: RulesProvider, requestKind: FormattingRequestKind): TextChange[] { const parent = findOutermostParent(position, expectedLastToken, sourceFile); if (!parent) { return []; @@ -294,7 +294,7 @@ namespace ts.formatting { * if parent is on the different line - its delta was already contributed * to the initial indentation. */ - function getOwnOrInheritedDelta(n: Node, options: FormatCodeOptions, sourceFile: SourceFile): number { + function getOwnOrInheritedDelta(n: Node, options: FormatCodeSettings, sourceFile: SourceFile): number { let previousLine = Constants.Unknown; let child: Node; while (n) { @@ -304,7 +304,7 @@ namespace ts.formatting { } if (SmartIndenter.shouldIndentChildNode(n, child)) { - return options.IndentSize; + return options.indentSize; } previousLine = line; @@ -316,7 +316,7 @@ namespace ts.formatting { function formatSpan(originalRange: TextRange, sourceFile: SourceFile, - options: FormatCodeOptions, + options: FormatCodeSettings, rulesProvider: RulesProvider, requestKind: FormattingRequestKind): TextChange[] { @@ -410,7 +410,7 @@ namespace ts.formatting { effectiveParentStartLine: number): Indentation { let indentation = inheritedIndentation; - let delta = SmartIndenter.shouldIndentChildNode(node) ? options.IndentSize : 0; + let delta = SmartIndenter.shouldIndentChildNode(node) ? options.indentSize : 0; if (effectiveParentStartLine === startLine) { // if node is located on the same line with the parent @@ -419,7 +419,7 @@ namespace ts.formatting { indentation = startLine === lastIndentedLine ? indentationOnLastIndentedLine : parentDynamicIndentation.getIndentation(); - delta = Math.min(options.IndentSize, parentDynamicIndentation.getDelta(node) + delta); + delta = Math.min(options.indentSize, parentDynamicIndentation.getDelta(node) + delta); } else if (indentation === Constants.Unknown) { if (SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile)) { @@ -503,14 +503,14 @@ namespace ts.formatting { recomputeIndentation: lineAdded => { if (node.parent && SmartIndenter.shouldIndentChildNode(node.parent, node)) { if (lineAdded) { - indentation += options.IndentSize; + indentation += options.indentSize; } else { - indentation -= options.IndentSize; + indentation -= options.indentSize; } if (SmartIndenter.shouldIndentChildNode(node)) { - delta = options.IndentSize; + delta = options.indentSize; } else { delta = 0; @@ -1036,7 +1036,7 @@ namespace ts.formatting { // edit should not be applied only if we have one line feed between elements const lineDelta = currentStartLine - previousStartLine; if (lineDelta !== 1) { - recordReplace(previousRange.end, currentRange.pos - previousRange.end, options.NewLineCharacter); + recordReplace(previousRange.end, currentRange.pos - previousRange.end, options.newLineCharacter); } break; case RuleAction.Space: @@ -1102,19 +1102,19 @@ namespace ts.formatting { let internedTabsIndentation: string[]; let internedSpacesIndentation: string[]; - export function getIndentationString(indentation: number, options: FormatCodeOptions): string { + export function getIndentationString(indentation: number, options: EditorSettings): string { // reset interned strings if FormatCodeOptions were changed const resetInternedStrings = - !internedSizes || (internedSizes.tabSize !== options.TabSize || internedSizes.indentSize !== options.IndentSize); + !internedSizes || (internedSizes.tabSize !== options.tabSize || internedSizes.indentSize !== options.indentSize); if (resetInternedStrings) { - internedSizes = { tabSize: options.TabSize, indentSize: options.IndentSize }; + internedSizes = { tabSize: options.tabSize, indentSize: options.indentSize }; internedTabsIndentation = internedSpacesIndentation = undefined; } - if (!options.ConvertTabsToSpaces) { - const tabs = Math.floor(indentation / options.TabSize); - const spaces = indentation - tabs * options.TabSize; + if (!options.convertTabsToSpaces) { + const tabs = Math.floor(indentation / options.tabSize); + const spaces = indentation - tabs * options.tabSize; let tabString: string; if (!internedTabsIndentation) { @@ -1132,14 +1132,14 @@ namespace ts.formatting { } else { let spacesString: string; - const quotient = Math.floor(indentation / options.IndentSize); - const remainder = indentation % options.IndentSize; + const quotient = Math.floor(indentation / options.indentSize); + const remainder = indentation % options.indentSize; if (!internedSpacesIndentation) { internedSpacesIndentation = []; } if (internedSpacesIndentation[quotient] === undefined) { - spacesString = repeat(" ", options.IndentSize * quotient); + spacesString = repeat(" ", options.indentSize * quotient); internedSpacesIndentation[quotient] = spacesString; } else { diff --git a/src/services/formatting/rulesProvider.ts b/src/services/formatting/rulesProvider.ts index d672a401d89..ba3c3e0d356 100644 --- a/src/services/formatting/rulesProvider.ts +++ b/src/services/formatting/rulesProvider.ts @@ -4,7 +4,7 @@ namespace ts.formatting { export class RulesProvider { private globalRules: Rules; - private options: ts.FormatCodeOptions; + private options: ts.FormatCodeSettings; private activeRules: Rule[]; private rulesMap: RulesMap; @@ -24,7 +24,7 @@ namespace ts.formatting { return this.rulesMap; } - public ensureUpToDate(options: ts.FormatCodeOptions) { + public ensureUpToDate(options: ts.FormatCodeSettings) { if (!this.options || !ts.compareDataObjects(this.options, options)) { const activeRules = this.createActiveRules(options); const rulesMap = RulesMap.create(activeRules); @@ -35,31 +35,31 @@ namespace ts.formatting { } } - private createActiveRules(options: ts.FormatCodeOptions): Rule[] { + private createActiveRules(options: ts.FormatCodeSettings): Rule[] { let rules = this.globalRules.HighPriorityCommonRules.slice(0); - if (options.InsertSpaceAfterCommaDelimiter) { + if (options.insertSpaceAfterCommaDelimiter) { rules.push(this.globalRules.SpaceAfterComma); } else { rules.push(this.globalRules.NoSpaceAfterComma); } - if (options.InsertSpaceAfterFunctionKeywordForAnonymousFunctions) { + if (options.insertSpaceAfterFunctionKeywordForAnonymousFunctions) { rules.push(this.globalRules.SpaceAfterAnonymousFunctionKeyword); } else { rules.push(this.globalRules.NoSpaceAfterAnonymousFunctionKeyword); } - if (options.InsertSpaceAfterKeywordsInControlFlowStatements) { + if (options.insertSpaceAfterKeywordsInControlFlowStatements) { rules.push(this.globalRules.SpaceAfterKeywordInControl); } else { rules.push(this.globalRules.NoSpaceAfterKeywordInControl); } - if (options.InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis) { + if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis) { rules.push(this.globalRules.SpaceAfterOpenParen); rules.push(this.globalRules.SpaceBeforeCloseParen); rules.push(this.globalRules.NoSpaceBetweenParens); @@ -70,7 +70,7 @@ namespace ts.formatting { rules.push(this.globalRules.NoSpaceBetweenParens); } - if (options.InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets) { + if (options.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets) { rules.push(this.globalRules.SpaceAfterOpenBracket); rules.push(this.globalRules.SpaceBeforeCloseBracket); rules.push(this.globalRules.NoSpaceBetweenBrackets); @@ -81,7 +81,7 @@ namespace ts.formatting { rules.push(this.globalRules.NoSpaceBetweenBrackets); } - if (options.InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces) { + if (options.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces) { rules.push(this.globalRules.SpaceAfterTemplateHeadAndMiddle); rules.push(this.globalRules.SpaceBeforeTemplateMiddleAndTail); } @@ -90,14 +90,14 @@ namespace ts.formatting { rules.push(this.globalRules.NoSpaceBeforeTemplateMiddleAndTail); } - if (options.InsertSpaceAfterSemicolonInForStatements) { + if (options.insertSpaceAfterSemicolonInForStatements) { rules.push(this.globalRules.SpaceAfterSemicolonInFor); } else { rules.push(this.globalRules.NoSpaceAfterSemicolonInFor); } - if (options.InsertSpaceBeforeAndAfterBinaryOperators) { + if (options.insertSpaceBeforeAndAfterBinaryOperators) { rules.push(this.globalRules.SpaceBeforeBinaryOperator); rules.push(this.globalRules.SpaceAfterBinaryOperator); } @@ -106,11 +106,11 @@ namespace ts.formatting { rules.push(this.globalRules.NoSpaceAfterBinaryOperator); } - if (options.PlaceOpenBraceOnNewLineForControlBlocks) { + if (options.placeOpenBraceOnNewLineForControlBlocks) { rules.push(this.globalRules.NewLineBeforeOpenBraceInControl); } - if (options.PlaceOpenBraceOnNewLineForFunctions) { + if (options.placeOpenBraceOnNewLineForFunctions) { rules.push(this.globalRules.NewLineBeforeOpenBraceInFunction); rules.push(this.globalRules.NewLineBeforeOpenBraceInTypeScriptDeclWithBlock); } diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index 23a1d937869..9c9d9eb9149 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -8,14 +8,14 @@ namespace ts.formatting { Unknown = -1 } - export function getIndentation(position: number, sourceFile: SourceFile, options: EditorOptions): number { + export function getIndentation(position: number, sourceFile: SourceFile, options: EditorSettings): number { if (position > sourceFile.text.length) { return 0; // past EOF } // no indentation when the indent style is set to none, // so we can return fast - if (options.IndentStyle === IndentStyle.None) { + if (options.indentStyle === IndentStyle.None) { return 0; } @@ -35,7 +35,7 @@ namespace ts.formatting { // indentation is first non-whitespace character in a previous line // for block indentation, we should look for a line which contains something that's not // whitespace. - if (options.IndentStyle === IndentStyle.Block) { + if (options.indentStyle === IndentStyle.Block) { // move backwards until we find a line with a non-whitespace character, // then find the first non-whitespace character for that line. @@ -75,7 +75,7 @@ namespace ts.formatting { indentationDelta = 0; } else { - indentationDelta = lineAtPosition !== currentStart.line ? options.IndentSize : 0; + indentationDelta = lineAtPosition !== currentStart.line ? options.indentSize : 0; } break; @@ -88,7 +88,7 @@ namespace ts.formatting { } actualIndentation = getLineIndentationWhenExpressionIsInMultiLine(current, sourceFile, options); if (actualIndentation !== Value.Unknown) { - return actualIndentation + options.IndentSize; + return actualIndentation + options.indentSize; } previous = current; @@ -103,7 +103,7 @@ namespace ts.formatting { return getIndentationForNodeWorker(current, currentStart, /*ignoreActualIndentationRange*/ undefined, indentationDelta, sourceFile, options); } - export function getIndentationForNode(n: Node, ignoreActualIndentationRange: TextRange, sourceFile: SourceFile, options: FormatCodeOptions): number { + export function getIndentationForNode(n: Node, ignoreActualIndentationRange: TextRange, sourceFile: SourceFile, options: EditorSettings): number { const start = sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile)); return getIndentationForNodeWorker(n, start, ignoreActualIndentationRange, /*indentationDelta*/ 0, sourceFile, options); } @@ -114,7 +114,7 @@ namespace ts.formatting { ignoreActualIndentationRange: TextRange, indentationDelta: number, sourceFile: SourceFile, - options: EditorOptions): number { + options: EditorSettings): number { let parent: Node = current.parent; let parentStart: LineAndCharacter; @@ -154,7 +154,7 @@ namespace ts.formatting { // increase indentation if parent node wants its content to be indented and parent and child nodes don't start on the same line if (shouldIndentChildNode(parent, current) && !parentAndChildShareLine) { - indentationDelta += options.IndentSize; + indentationDelta += options.indentSize; } current = parent; @@ -178,7 +178,7 @@ namespace ts.formatting { /* * Function returns Value.Unknown if indentation cannot be determined */ - function getActualIndentationForListItemBeforeComma(commaToken: Node, sourceFile: SourceFile, options: EditorOptions): number { + function getActualIndentationForListItemBeforeComma(commaToken: Node, sourceFile: SourceFile, options: EditorSettings): number { // previous token is comma that separates items in list - find the previous item and try to derive indentation from it const commaItemInfo = findListItemInfo(commaToken); if (commaItemInfo && commaItemInfo.listItemIndex > 0) { @@ -198,7 +198,7 @@ namespace ts.formatting { currentLineAndChar: LineAndCharacter, parentAndChildShareLine: boolean, sourceFile: SourceFile, - options: EditorOptions): number { + options: EditorSettings): number { // actual indentation is used for statements\declarations if one of cases below is true: // - parent is SourceFile - by default immediate children of SourceFile are not indented except when user indents them manually @@ -305,7 +305,7 @@ namespace ts.formatting { return undefined; } - function getActualIndentationForListItem(node: Node, sourceFile: SourceFile, options: EditorOptions): number { + function getActualIndentationForListItem(node: Node, sourceFile: SourceFile, options: EditorSettings): number { const containingList = getContainingList(node, sourceFile); return containingList ? getActualIndentationFromList(containingList) : Value.Unknown; @@ -315,7 +315,7 @@ namespace ts.formatting { } } - function getLineIndentationWhenExpressionIsInMultiLine(node: Node, sourceFile: SourceFile, options: EditorOptions): number { + function getLineIndentationWhenExpressionIsInMultiLine(node: Node, sourceFile: SourceFile, options: EditorSettings): number { // actual indentation should not be used when: // - node is close parenthesis - this is the end of the expression if (node.kind === SyntaxKind.CloseParenToken) { @@ -363,7 +363,7 @@ namespace ts.formatting { } } - function deriveActualIndentationFromList(list: Node[], index: number, sourceFile: SourceFile, options: EditorOptions): number { + function deriveActualIndentationFromList(list: Node[], index: number, sourceFile: SourceFile, options: EditorSettings): number { Debug.assert(index >= 0 && index < list.length); const node = list[index]; @@ -385,7 +385,7 @@ namespace ts.formatting { return Value.Unknown; } - function findColumnForFirstNonWhitespaceCharacterInLine(lineAndCharacter: LineAndCharacter, sourceFile: SourceFile, options: EditorOptions): number { + function findColumnForFirstNonWhitespaceCharacterInLine(lineAndCharacter: LineAndCharacter, sourceFile: SourceFile, options: EditorSettings): number { const lineStart = sourceFile.getPositionOfLineAndCharacter(lineAndCharacter.line, 0); return findFirstNonWhitespaceColumn(lineStart, lineStart + lineAndCharacter.character, sourceFile, options); } @@ -397,7 +397,7 @@ namespace ts.formatting { value of 'character' for '$' is 3 value of 'column' for '$' is 6 (assuming that tab size is 4) */ - export function findFirstNonWhitespaceCharacterAndColumn(startPos: number, endPos: number, sourceFile: SourceFile, options: EditorOptions) { + export function findFirstNonWhitespaceCharacterAndColumn(startPos: number, endPos: number, sourceFile: SourceFile, options: EditorSettings) { let character = 0; let column = 0; for (let pos = startPos; pos < endPos; pos++) { @@ -407,7 +407,7 @@ namespace ts.formatting { } if (ch === CharacterCodes.tab) { - column += options.TabSize + (column % options.TabSize); + column += options.tabSize + (column % options.tabSize); } else { column++; @@ -418,7 +418,7 @@ namespace ts.formatting { return { column, character }; } - export function findFirstNonWhitespaceColumn(startPos: number, endPos: number, sourceFile: SourceFile, options: EditorOptions): number { + export function findFirstNonWhitespaceColumn(startPos: number, endPos: number, sourceFile: SourceFile, options: EditorSettings): number { return findFirstNonWhitespaceCharacterAndColumn(startPos, endPos, sourceFile, options).column; } diff --git a/src/services/services.ts b/src/services/services.ts index 9209fc157c2..5e6abd886bb 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1154,11 +1154,11 @@ namespace ts { getOutliningSpans(fileName: string): OutliningSpan[]; getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[]; getBraceMatchingAtPosition(fileName: string, position: number): TextSpan[]; - getIndentationAtPosition(fileName: string, position: number, options: EditorOptions): number; + getIndentationAtPosition(fileName: string, position: number, options: EditorOptions | EditorSettings): number; - getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions): TextChange[]; - getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions): TextChange[]; - getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): TextChange[]; + getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions | FormatCodeSettings): TextChange[]; + getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[]; + getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions | FormatCodeSettings): TextChange[]; getDocCommentTemplateAtPosition(fileName: string, position: number): TextInsertion; @@ -1258,6 +1258,7 @@ namespace ts { containerKind: string; } + /* @deprecated - consider using EditorSettings instead */ export interface EditorOptions { IndentSize: number; TabSize: number; @@ -1266,12 +1267,21 @@ namespace ts { IndentStyle: IndentStyle; } + export interface EditorSettings { + indentSize: number; + tabSize: number; + newLineCharacter: string; + convertTabsToSpaces: boolean; + indentStyle: IndentStyle; + } + export enum IndentStyle { None = 0, Block = 1, Smart = 2, } + /* @deprecated - consider using FormatCodeSettings instead */ export interface FormatCodeOptions extends EditorOptions { InsertSpaceAfterCommaDelimiter: boolean; InsertSpaceAfterSemicolonInForStatements: boolean; @@ -1283,9 +1293,50 @@ namespace ts { InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: boolean; PlaceOpenBraceOnNewLineForFunctions: boolean; PlaceOpenBraceOnNewLineForControlBlocks: boolean; - [s: string]: boolean | number | string; } + export interface FormatCodeSettings extends EditorSettings { + insertSpaceAfterCommaDelimiter: boolean; + insertSpaceAfterSemicolonInForStatements: boolean; + insertSpaceBeforeAndAfterBinaryOperators: boolean; + insertSpaceAfterKeywordsInControlFlowStatements: boolean; + insertSpaceAfterFunctionKeywordForAnonymousFunctions: boolean; + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: boolean; + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: boolean; + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: boolean; + placeOpenBraceOnNewLineForFunctions: boolean; + placeOpenBraceOnNewLineForControlBlocks: boolean; + } + + /* @internal */ + export function toEditorSettings(options: FormatCodeOptions | FormatCodeSettings): FormatCodeSettings; + export function toEditorSettings(options: EditorOptions | EditorSettings): EditorSettings; + export function toEditorSettings(optionsAsMap: Map): Map { + let allPropertiesAreCamelCased = true; + for (const key in optionsAsMap) { + if (hasProperty(optionsAsMap, key) && !isCamelCase(key)) { + allPropertiesAreCamelCased = false; + break; + } + } + if (allPropertiesAreCamelCased) { + return optionsAsMap; + } + const settings: Map = {}; + for (const key in optionsAsMap) { + if (hasProperty(optionsAsMap, key)) { + const newKey = isCamelCase(key) ? key : key.charAt(0).toLowerCase() + key.substr(1); + settings[newKey] = optionsAsMap[key]; + } + } + return settings; + } + + function isCamelCase(s: string) { + return !s.length || s.charAt(0) === s.charAt(0).toLowerCase(); + } + + export interface DefinitionInfo { fileName: string; textSpan: TextSpan; @@ -2949,7 +3000,7 @@ namespace ts { return sourceFile; } - function getRuleProvider(options: FormatCodeOptions) { + function getRuleProvider(options: FormatCodeSettings) { // Ensure rules are initialized and up to date wrt to formatting options if (!ruleProvider) { ruleProvider = new formatting.RulesProvider(); @@ -7632,40 +7683,44 @@ namespace ts { } } - function getIndentationAtPosition(fileName: string, position: number, editorOptions: EditorOptions) { + function getIndentationAtPosition(fileName: string, position: number, optionsOrSettings: EditorOptions | EditorSettings) { let start = new Date().getTime(); + const settings = toEditorSettings(optionsOrSettings); const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); log("getIndentationAtPosition: getCurrentSourceFile: " + (new Date().getTime() - start)); start = new Date().getTime(); - const result = formatting.SmartIndenter.getIndentation(position, sourceFile, editorOptions); + const result = formatting.SmartIndenter.getIndentation(position, sourceFile, settings); log("getIndentationAtPosition: computeIndentation : " + (new Date().getTime() - start)); return result; } - function getFormattingEditsForRange(fileName: string, start: number, end: number, options: FormatCodeOptions): TextChange[] { + function getFormattingEditsForRange(fileName: string, start: number, end: number, optionsOrSettings: FormatCodeOptions | FormatCodeSettings): TextChange[] { + const settings = toEditorSettings(optionsOrSettings); const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); - return formatting.formatSelection(start, end, sourceFile, getRuleProvider(options), options); + return formatting.formatSelection(start, end, sourceFile, getRuleProvider(settings), settings); } - function getFormattingEditsForDocument(fileName: string, options: FormatCodeOptions): TextChange[] { + function getFormattingEditsForDocument(fileName: string, optionsOrSettings: FormatCodeOptions | FormatCodeSettings): TextChange[] { + const settings = toEditorSettings(optionsOrSettings); const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); - return formatting.formatDocument(sourceFile, getRuleProvider(options), options); + return formatting.formatDocument(sourceFile, getRuleProvider(settings), settings); } - function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: FormatCodeOptions): TextChange[] { + function getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, optionsOrSettings: FormatCodeOptions | FormatCodeSettings): TextChange[] { + const settings = toEditorSettings(optionsOrSettings); const sourceFile = syntaxTreeCache.getCurrentSourceFile(fileName); if (key === "}") { - return formatting.formatOnClosingCurly(position, sourceFile, getRuleProvider(options), options); + return formatting.formatOnClosingCurly(position, sourceFile, getRuleProvider(settings), settings); } else if (key === ";") { - return formatting.formatOnSemicolon(position, sourceFile, getRuleProvider(options), options); + return formatting.formatOnSemicolon(position, sourceFile, getRuleProvider(settings), settings); } else if (key === "\n") { - return formatting.formatOnEnter(position, sourceFile, getRuleProvider(options), options); + return formatting.formatOnEnter(position, sourceFile, getRuleProvider(settings), settings); } return [];