vendor tsgo
This commit is contained in:
99
tools/tsgo/internal/ls/autoinsert.go
Normal file
99
tools/tsgo/internal/ls/autoinsert.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package ls
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/astnav"
|
||||
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
|
||||
"github.com/microsoft/typescript-go/internal/scanner"
|
||||
)
|
||||
|
||||
func (l *LanguageService) ProvideOnAutoInsert(ctx context.Context, params *lsproto.VSOnAutoInsertParams) (lsproto.VSOnAutoInsertResponse, error) {
|
||||
if l.UserPreferences().EnableAutoClosingTags.IsFalse() {
|
||||
return lsproto.VSOnAutoInsertResponse{}, nil
|
||||
}
|
||||
if params.VSCh != ">" {
|
||||
return lsproto.VSOnAutoInsertResponse{}, nil
|
||||
}
|
||||
|
||||
_, sourceFile := l.getProgramAndFile(params.VSTextDocument.Uri)
|
||||
position := l.converters.LineAndCharacterToPosition(sourceFile, params.VSPosition)
|
||||
|
||||
token := astnav.FindPrecedingToken(sourceFile, int(position))
|
||||
if token == nil {
|
||||
return lsproto.VSOnAutoInsertResponse{}, nil
|
||||
}
|
||||
|
||||
var closingText string
|
||||
var element *ast.Node
|
||||
if token.Kind == ast.KindGreaterThanToken && ast.IsJsxOpeningElement(token.Parent) {
|
||||
element = token.Parent.Parent
|
||||
} else if ast.IsJsxText(token) && ast.IsJsxElement(token.Parent) {
|
||||
element = token.Parent
|
||||
}
|
||||
|
||||
if element != nil && isUnclosedTag(element.AsJsxElement()) {
|
||||
tagNameNode := element.AsJsxElement().OpeningElement.TagName()
|
||||
// Slight divergence from Strada - we don't use the verbatim text from the opening tag.
|
||||
closingText = "</" + ast.EntityNameToString(tagNameNode, scanner.GetTextOfNode) + ">"
|
||||
} else {
|
||||
var fragment *ast.Node
|
||||
if token.Kind == ast.KindGreaterThanToken && ast.IsJsxOpeningFragment(token.Parent) {
|
||||
fragment = token.Parent.Parent
|
||||
} else if ast.IsJsxText(token) && ast.IsJsxFragment(token.Parent) {
|
||||
fragment = token.Parent
|
||||
}
|
||||
|
||||
if fragment != nil && isUnclosedFragment(fragment.AsJsxFragment()) {
|
||||
closingText = "</>"
|
||||
}
|
||||
}
|
||||
|
||||
if closingText == "" {
|
||||
return lsproto.VSOnAutoInsertResponse{}, nil
|
||||
}
|
||||
|
||||
return lsproto.VSOnAutoInsertResponse{
|
||||
VSOnAutoInsertResponseItem: &lsproto.VSOnAutoInsertResponseItem{
|
||||
VSTextEditFormat: lsproto.InsertTextFormatSnippet,
|
||||
VSTextEdit: &lsproto.TextEdit{
|
||||
Range: lsproto.Range{Start: params.VSPosition, End: params.VSPosition},
|
||||
// Tag names can contain `$` (valid JSX identifier characters), so
|
||||
// escape the closing text to avoid being interpreted as a snippet
|
||||
// placeholder/variable.
|
||||
NewText: "$0" + escapeSnippetText(closingText),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func isUnclosedTag(node *ast.JsxElement) bool {
|
||||
openingElement := node.OpeningElement
|
||||
closingElement := node.ClosingElement
|
||||
if !ast.TagNamesAreEquivalent(openingElement.TagName(), closingElement.TagName()) {
|
||||
return true
|
||||
}
|
||||
|
||||
parent := node.Parent
|
||||
if ast.IsJsxElement(parent) {
|
||||
parent := parent.AsJsxElement()
|
||||
return ast.TagNamesAreEquivalent(openingElement.TagName(), parent.OpeningElement.TagName()) && isUnclosedTag(parent)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isUnclosedFragment(node *ast.JsxFragment) bool {
|
||||
closingFragment := node.ClosingFragment
|
||||
if closingFragment.Flags&ast.NodeFlagsThisNodeHasError != 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
parent := node.Parent
|
||||
if ast.IsJsxFragment(parent) && isUnclosedFragment(parent.AsJsxFragment()) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user