216 lines
8.2 KiB
Go
216 lines
8.2 KiB
Go
package format
|
|
|
|
import (
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
|
|
)
|
|
|
|
type IndentStyle int
|
|
|
|
const (
|
|
IndentStyleNone IndentStyle = iota
|
|
IndentStyleBlock
|
|
IndentStyleSmart
|
|
)
|
|
|
|
type SemicolonPreference string
|
|
|
|
const (
|
|
SemicolonPreferenceIgnore SemicolonPreference = "ignore"
|
|
SemicolonPreferenceInsert SemicolonPreference = "insert"
|
|
SemicolonPreferenceRemove SemicolonPreference = "remove"
|
|
)
|
|
|
|
type EditorSettings struct {
|
|
BaseIndentSize int
|
|
IndentSize int
|
|
TabSize int
|
|
NewLineCharacter string
|
|
ConvertTabsToSpaces bool
|
|
IndentStyle IndentStyle
|
|
TrimTrailingWhitespace bool
|
|
}
|
|
|
|
type FormatCodeSettings struct {
|
|
EditorSettings
|
|
InsertSpaceAfterCommaDelimiter core.Tristate
|
|
InsertSpaceAfterSemicolonInForStatements core.Tristate
|
|
InsertSpaceBeforeAndAfterBinaryOperators core.Tristate
|
|
InsertSpaceAfterConstructor core.Tristate
|
|
InsertSpaceAfterKeywordsInControlFlowStatements core.Tristate
|
|
InsertSpaceAfterFunctionKeywordForAnonymousFunctions core.Tristate
|
|
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis core.Tristate
|
|
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets core.Tristate
|
|
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces core.Tristate
|
|
InsertSpaceAfterOpeningAndBeforeClosingEmptyBraces core.Tristate
|
|
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces core.Tristate
|
|
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces core.Tristate
|
|
InsertSpaceAfterTypeAssertion core.Tristate
|
|
InsertSpaceBeforeFunctionParenthesis core.Tristate
|
|
PlaceOpenBraceOnNewLineForFunctions core.Tristate
|
|
PlaceOpenBraceOnNewLineForControlBlocks core.Tristate
|
|
InsertSpaceBeforeTypeAnnotation core.Tristate
|
|
IndentMultiLineObjectLiteralBeginningOnBlankLine core.Tristate
|
|
Semicolons SemicolonPreference
|
|
IndentSwitchCase core.Tristate
|
|
}
|
|
|
|
func GetDefaultFormatCodeSettings(newLineCharacter string) *FormatCodeSettings {
|
|
return &FormatCodeSettings{
|
|
EditorSettings: EditorSettings{
|
|
IndentSize: 4,
|
|
TabSize: 4,
|
|
NewLineCharacter: newLineCharacter,
|
|
ConvertTabsToSpaces: true,
|
|
IndentStyle: IndentStyleSmart,
|
|
TrimTrailingWhitespace: true,
|
|
},
|
|
InsertSpaceAfterConstructor: core.TSFalse,
|
|
InsertSpaceAfterCommaDelimiter: core.TSTrue,
|
|
InsertSpaceAfterSemicolonInForStatements: core.TSTrue,
|
|
InsertSpaceBeforeAndAfterBinaryOperators: core.TSTrue,
|
|
InsertSpaceAfterKeywordsInControlFlowStatements: core.TSTrue,
|
|
InsertSpaceAfterFunctionKeywordForAnonymousFunctions: core.TSFalse,
|
|
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: core.TSFalse,
|
|
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: core.TSFalse,
|
|
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: core.TSTrue,
|
|
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: core.TSFalse,
|
|
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: core.TSFalse,
|
|
InsertSpaceBeforeFunctionParenthesis: core.TSFalse,
|
|
PlaceOpenBraceOnNewLineForFunctions: core.TSFalse,
|
|
PlaceOpenBraceOnNewLineForControlBlocks: core.TSFalse,
|
|
Semicolons: SemicolonPreferenceIgnore,
|
|
IndentSwitchCase: core.TSTrue,
|
|
}
|
|
}
|
|
|
|
type formattingContext struct {
|
|
currentTokenSpan TextRangeWithKind
|
|
nextTokenSpan TextRangeWithKind
|
|
contextNode *ast.Node
|
|
currentTokenParent *ast.Node
|
|
nextTokenParent *ast.Node
|
|
|
|
contextNodeAllOnSameLine core.Tristate
|
|
nextNodeAllOnSameLine core.Tristate
|
|
tokensAreOnSameLine core.Tristate
|
|
contextNodeBlockIsOnOneLine core.Tristate
|
|
nextNodeBlockIsOnOneLine core.Tristate
|
|
|
|
SourceFile *ast.SourceFile
|
|
FormattingRequestKind FormatRequestKind
|
|
Options *FormatCodeSettings
|
|
|
|
scanner *scanner.Scanner
|
|
}
|
|
|
|
func NewFormattingContext(file *ast.SourceFile, kind FormatRequestKind, options *FormatCodeSettings) *formattingContext {
|
|
res := &formattingContext{
|
|
SourceFile: file,
|
|
FormattingRequestKind: kind,
|
|
Options: options,
|
|
scanner: scanner.NewScanner(),
|
|
}
|
|
res.scanner.SetText(file.Text())
|
|
res.scanner.SetSkipTrivia(true)
|
|
return res
|
|
}
|
|
|
|
func (this *formattingContext) UpdateContext(cur TextRangeWithKind, curParent *ast.Node, next TextRangeWithKind, nextParent *ast.Node, commonParent *ast.Node) {
|
|
if curParent == nil {
|
|
panic("nil current range node parent in update context")
|
|
}
|
|
if nextParent == nil {
|
|
panic("nil next range node parent in update context")
|
|
}
|
|
if commonParent == nil {
|
|
panic("nil common parent node in update context")
|
|
}
|
|
this.currentTokenSpan = cur
|
|
this.currentTokenParent = curParent
|
|
this.nextTokenSpan = next
|
|
this.nextTokenParent = nextParent
|
|
this.contextNode = commonParent
|
|
|
|
// drop cached results
|
|
this.contextNodeAllOnSameLine = core.TSUnknown
|
|
this.nextNodeAllOnSameLine = core.TSUnknown
|
|
this.tokensAreOnSameLine = core.TSUnknown
|
|
this.contextNodeBlockIsOnOneLine = core.TSUnknown
|
|
this.nextNodeBlockIsOnOneLine = core.TSUnknown
|
|
}
|
|
|
|
func (this *formattingContext) rangeIsOnOneLine(node core.TextRange) core.Tristate {
|
|
if rangeIsOnOneLine(node, this.SourceFile) {
|
|
return core.TSTrue
|
|
}
|
|
return core.TSFalse
|
|
}
|
|
|
|
func (this *formattingContext) nodeIsOnOneLine(node *ast.Node) core.Tristate {
|
|
return this.rangeIsOnOneLine(withTokenStart(node, this.SourceFile))
|
|
}
|
|
|
|
func withTokenStart(loc *ast.Node, file *ast.SourceFile) core.TextRange {
|
|
startPos := scanner.GetTokenPosOfNode(loc, file, false)
|
|
return core.NewTextRange(startPos, loc.End())
|
|
}
|
|
|
|
func (this *formattingContext) blockIsOnOneLine(node *ast.Node) core.Tristate {
|
|
// In strada, this relies on token child manifesting - we just use the scanner here,
|
|
// so this will have a differing performance profile. Is this OK? Needs profiling to know.
|
|
this.scanner.ResetPos(node.Pos())
|
|
end := node.End()
|
|
firstOpenBrace := -1
|
|
lastCloseBrace := -1
|
|
for this.scanner.TokenEnd() < end {
|
|
// tokenStart instead of tokenfullstart to skip trivia
|
|
if firstOpenBrace == -1 && this.scanner.Token() == ast.KindOpenBraceToken {
|
|
firstOpenBrace = this.scanner.TokenStart()
|
|
} else if this.scanner.Token() == ast.KindCloseBraceToken {
|
|
lastCloseBrace = this.scanner.TokenStart()
|
|
}
|
|
this.scanner.Scan()
|
|
}
|
|
if firstOpenBrace != -1 && lastCloseBrace != -1 {
|
|
return this.rangeIsOnOneLine(core.NewTextRange(firstOpenBrace, lastCloseBrace))
|
|
}
|
|
return core.TSFalse
|
|
}
|
|
|
|
func (this *formattingContext) ContextNodeAllOnSameLine() bool {
|
|
if this.contextNodeAllOnSameLine == core.TSUnknown {
|
|
this.contextNodeAllOnSameLine = this.nodeIsOnOneLine(this.contextNode)
|
|
}
|
|
return this.contextNodeAllOnSameLine == core.TSTrue
|
|
}
|
|
|
|
func (this *formattingContext) NextNodeAllOnSameLine() bool {
|
|
if this.nextNodeAllOnSameLine == core.TSUnknown {
|
|
this.nextNodeAllOnSameLine = this.nodeIsOnOneLine(this.nextTokenParent)
|
|
}
|
|
return this.nextNodeAllOnSameLine == core.TSTrue
|
|
}
|
|
|
|
func (this *formattingContext) TokensAreOnSameLine() bool {
|
|
if this.tokensAreOnSameLine == core.TSUnknown {
|
|
this.tokensAreOnSameLine = this.rangeIsOnOneLine(core.NewTextRange(this.currentTokenSpan.Loc.Pos(), this.nextTokenSpan.Loc.End()))
|
|
}
|
|
return this.tokensAreOnSameLine == core.TSTrue
|
|
}
|
|
|
|
func (this *formattingContext) ContextNodeBlockIsOnOneLine() bool {
|
|
if this.contextNodeBlockIsOnOneLine == core.TSUnknown {
|
|
this.contextNodeBlockIsOnOneLine = this.blockIsOnOneLine(this.contextNode)
|
|
}
|
|
return this.contextNodeBlockIsOnOneLine == core.TSTrue
|
|
}
|
|
|
|
func (this *formattingContext) NextNodeBlockIsOnOneLine() bool {
|
|
if this.nextNodeBlockIsOnOneLine == core.TSUnknown {
|
|
this.nextNodeBlockIsOnOneLine = this.blockIsOnOneLine(this.nextTokenParent)
|
|
}
|
|
return this.nextNodeBlockIsOnOneLine == core.TSTrue
|
|
}
|