Skip to content

Commit 2e36e7d

Browse files
gabrittojakebailey
andauthored
Basic completion structure + dot completions (#811)
Co-authored-by: Jake Bailey <[email protected]>
1 parent 60f706c commit 2e36e7d

25 files changed

+4189
-312
lines changed

internal/ast/ast.go

+24-2
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,18 @@ func (n *Node) Children() *NodeList {
749749
panic("Unhandled case in Node.Children: " + n.Kind.String())
750750
}
751751

752+
func (n *Node) ModuleSpecifier() *Expression {
753+
switch n.Kind {
754+
case KindImportDeclaration:
755+
return n.AsImportDeclaration().ModuleSpecifier
756+
case KindExportDeclaration:
757+
return n.AsExportDeclaration().ModuleSpecifier
758+
case KindJSDocImportTag:
759+
return n.AsJSDocImportTag().ModuleSpecifier
760+
}
761+
panic("Unhandled case in Node.ModuleSpecifier: " + n.Kind.String())
762+
}
763+
752764
// Determines if `n` contains `descendant` by walking up the `Parent` pointers from `descendant`. This method panics if
753765
// `descendant` or one of its ancestors is not parented except when that node is a `SourceFile`.
754766
func (n *Node) Contains(descendant *Node) bool {
@@ -1655,6 +1667,10 @@ type (
16551667
JSDocComment = Node // JSDocText | JSDocLink | JSDocLinkCode | JSDocLinkPlain;
16561668
JSDocTag = Node // Node with JSDocTagBase
16571669
SignatureDeclaration = Node // CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | AccessorDeclaration | FunctionExpression | ArrowFunction;
1670+
StringLiteralLike = Node // StringLiteral | NoSubstitutionTemplateLiteral
1671+
AnyValidImportOrReExport = Node // (ImportDeclaration | ExportDeclaration | JSDocImportTag) & { moduleSpecifier: StringLiteral } | ImportEqualsDeclaration & { moduleReference: ExternalModuleReference & { expression: StringLiteral }} | RequireOrImportCall | ValidImportTypeNode
1672+
ValidImportTypeNode = Node // ImportTypeNode & { argument: LiteralTypeNode & { literal: StringLiteral } }
1673+
NumericOrStringLikeLiteral = Node // StringLiteralLike | NumericLiteral
16581674
)
16591675

16601676
// Aliases for node singletons
@@ -1673,6 +1689,7 @@ type (
16731689
CatchClauseNode = Node
16741690
CaseBlockNode = Node
16751691
CaseOrDefaultClauseNode = Node
1692+
CaseClauseNode = Node
16761693
VariableDeclarationNode = Node
16771694
VariableDeclarationListNode = Node
16781695
BindingElementNode = Node
@@ -1695,6 +1712,7 @@ type (
16951712
JsxOpeningFragmentNode = Node
16961713
JsxClosingFragmentNode = Node
16971714
SourceFileNode = Node
1715+
PropertyAccessExpressionNode = Node
16981716
)
16991717

17001718
type (
@@ -8219,6 +8237,10 @@ func (node *JsxFragment) computeSubtreeFacts() SubtreeFacts {
82198237
SubtreeContainsJsx
82208238
}
82218239

8240+
func IsJsxFragment(node *Node) bool {
8241+
return node.Kind == KindJsxFragment
8242+
}
8243+
82228244
/// The opening element of a <>...</> JsxFragment
82238245

82248246
type JsxOpeningFragment struct {
@@ -9446,7 +9468,7 @@ func (node *JSDocThisTag) Clone(f NodeFactoryCoercible) *Node {
94469468
type JSDocImportTag struct {
94479469
JSDocTagBase
94489470
ImportClause *Declaration
9449-
ModuleSpecifier *Node
9471+
ModuleSpecifier *Expression
94509472
Attributes *Node
94519473
}
94529474

@@ -9947,7 +9969,7 @@ func (node *SourceFile) GetOrCreateToken(
99479969
panic(fmt.Sprintf("Token cache mismatch: %v != %v", token.Kind, kind))
99489970
}
99499971
if token.Parent != parent {
9950-
panic("Token cache mismatch: parent")
9972+
panic(fmt.Sprintf("Token cache mismatch: parent. Expected parent of kind %v, got %v", token.Parent.Kind, parent.Kind))
99519973
}
99529974
return token
99539975
}

internal/ast/nodeflags.go

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ const (
4343
NodeFlagsJsonFile NodeFlags = 1 << 25 // If node was parsed in a Json
4444
NodeFlagsDeprecated NodeFlags = 1 << 26 // If has '@deprecated' JSDoc tag
4545

46+
NodeFlagsSkipDirectInference NodeFlags = 1 << 27 // If the node should skip direct type inference.
47+
4648
NodeFlagsBlockScoped = NodeFlagsLet | NodeFlagsConst | NodeFlagsUsing
4749
NodeFlagsConstant = NodeFlagsConst | NodeFlagsUsing
4850
NodeFlagsAwaitUsing = NodeFlagsConst | NodeFlagsUsing // Variable declaration (NOTE: on a single node these flags would otherwise be mutually exclusive)

internal/ast/utilities.go

+134-2
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ func IsFunctionLikeDeclaration(node *Node) bool {
510510
return node != nil && isFunctionLikeDeclarationKind(node.Kind)
511511
}
512512

513-
func isFunctionLikeKind(kind Kind) bool {
513+
func IsFunctionLikeKind(kind Kind) bool {
514514
switch kind {
515515
case KindMethodSignature,
516516
KindCallSignature,
@@ -527,7 +527,7 @@ func isFunctionLikeKind(kind Kind) bool {
527527
// Determines if a node is function- or signature-like.
528528
func IsFunctionLike(node *Node) bool {
529529
// TODO(rbuckton): Move `node != nil` test to call sites
530-
return node != nil && isFunctionLikeKind(node.Kind)
530+
return node != nil && IsFunctionLikeKind(node.Kind)
531531
}
532532

533533
func IsFunctionLikeOrClassStaticBlockDeclaration(node *Node) bool {
@@ -927,6 +927,13 @@ const (
927927
FindAncestorQuit
928928
)
929929

930+
func ToFindAncestorResult(b bool) FindAncestorResult {
931+
if b {
932+
return FindAncestorTrue
933+
}
934+
return FindAncestorFalse
935+
}
936+
930937
// Walks up the parents of a node to find the ancestor that matches the callback
931938
func FindAncestorOrQuit(node *Node, callback func(*Node) FindAncestorResult) *Node {
932939
for node != nil {
@@ -1966,6 +1973,7 @@ func TryGetTextOfPropertyName(name *Node) (string, bool) {
19661973
return "", false
19671974
}
19681975

1976+
// True if node is of a JSDoc kind that may contain comment text.
19691977
func IsJSDocCommentContainingNode(node *Node) bool {
19701978
return node.Kind == KindJSDoc ||
19711979
node.Kind == KindJSDocText ||
@@ -2651,3 +2659,127 @@ func GetPragmaArgument(pragma *Pragma, name string) string {
26512659
}
26522660
return ""
26532661
}
2662+
2663+
func IsCheckJSEnabledForFile(sourceFile *SourceFile, compilerOptions *core.CompilerOptions) bool {
2664+
if sourceFile.CheckJsDirective != nil {
2665+
return sourceFile.CheckJsDirective.Enabled
2666+
}
2667+
return compilerOptions.CheckJs == core.TSTrue
2668+
}
2669+
2670+
func GetLeftmostAccessExpression(expr *Node) *Node {
2671+
for IsAccessExpression(expr) {
2672+
expr = expr.Expression()
2673+
}
2674+
return expr
2675+
}
2676+
2677+
func IsTypeOnlyImportDeclaration(node *Node) bool {
2678+
switch node.Kind {
2679+
case KindImportSpecifier:
2680+
return node.AsImportSpecifier().IsTypeOnly || node.Parent.Parent.AsImportClause().IsTypeOnly
2681+
case KindNamespaceImport:
2682+
return node.Parent.AsImportClause().IsTypeOnly
2683+
case KindImportClause:
2684+
return node.AsImportClause().IsTypeOnly
2685+
case KindImportEqualsDeclaration:
2686+
return node.AsImportEqualsDeclaration().IsTypeOnly
2687+
}
2688+
return false
2689+
}
2690+
2691+
func isTypeOnlyExportDeclaration(node *Node) bool {
2692+
switch node.Kind {
2693+
case KindExportSpecifier:
2694+
return node.AsExportSpecifier().IsTypeOnly || node.Parent.Parent.AsExportDeclaration().IsTypeOnly
2695+
case KindExportDeclaration:
2696+
d := node.AsExportDeclaration()
2697+
return d.IsTypeOnly && d.ModuleSpecifier != nil && d.ExportClause == nil
2698+
case KindNamespaceExport:
2699+
return node.Parent.AsExportDeclaration().IsTypeOnly
2700+
}
2701+
return false
2702+
}
2703+
2704+
func IsTypeOnlyImportOrExportDeclaration(node *Node) bool {
2705+
return IsTypeOnlyImportDeclaration(node) || isTypeOnlyExportDeclaration(node)
2706+
}
2707+
2708+
func GetSourceFileOfModule(module *Symbol) *SourceFile {
2709+
declaration := module.ValueDeclaration
2710+
if declaration == nil {
2711+
declaration = getNonAugmentationDeclaration(module)
2712+
}
2713+
return GetSourceFileOfNode(declaration)
2714+
}
2715+
2716+
func getNonAugmentationDeclaration(symbol *Symbol) *Node {
2717+
return core.Find(symbol.Declarations, func(d *Node) bool {
2718+
return !IsExternalModuleAugmentation(d) && !IsGlobalScopeAugmentation(d)
2719+
})
2720+
}
2721+
2722+
func IsExternalModuleAugmentation(node *Node) bool {
2723+
return IsAmbientModule(node) && IsModuleAugmentationExternal(node)
2724+
}
2725+
2726+
func GetClassLikeDeclarationOfSymbol(symbol *Symbol) *Node {
2727+
return core.Find(symbol.Declarations, IsClassLike)
2728+
}
2729+
2730+
func GetLanguageVariant(scriptKind core.ScriptKind) core.LanguageVariant {
2731+
switch scriptKind {
2732+
case core.ScriptKindTSX, core.ScriptKindJSX, core.ScriptKindJS, core.ScriptKindJSON:
2733+
// .tsx and .jsx files are treated as jsx language variant.
2734+
return core.LanguageVariantJSX
2735+
}
2736+
return core.LanguageVariantStandard
2737+
}
2738+
2739+
func IsCallLikeExpression(node *Node) bool {
2740+
switch node.Kind {
2741+
case KindJsxOpeningElement, KindJsxSelfClosingElement, KindCallExpression, KindNewExpression,
2742+
KindTaggedTemplateExpression, KindDecorator:
2743+
return true
2744+
}
2745+
return false
2746+
}
2747+
2748+
func IsCallLikeOrFunctionLikeExpression(node *Node) bool {
2749+
return IsCallLikeExpression(node) || IsFunctionExpressionOrArrowFunction(node)
2750+
}
2751+
2752+
func NodeHasKind(node *Node, kind Kind) bool {
2753+
if node == nil {
2754+
return false
2755+
}
2756+
return node.Kind == kind
2757+
}
2758+
2759+
func IsContextualKeyword(token Kind) bool {
2760+
return KindFirstContextualKeyword <= token && token <= KindLastContextualKeyword
2761+
}
2762+
2763+
func IsThisInTypeQuery(node *Node) bool {
2764+
if !IsThisIdentifier(node) {
2765+
return false
2766+
}
2767+
for IsQualifiedName(node.Parent) && node.Parent.AsQualifiedName().Left == node {
2768+
node = node.Parent
2769+
}
2770+
return node.Parent.Kind == KindTypeQuery
2771+
}
2772+
2773+
// Gets whether a bound `VariableDeclaration` or `VariableDeclarationList` is part of a `let` declaration.
2774+
func IsLet(node *Node) bool {
2775+
return GetCombinedNodeFlags(node)&NodeFlagsBlockScoped == NodeFlagsLet
2776+
}
2777+
2778+
func IsClassMemberModifier(token Kind) bool {
2779+
return IsParameterPropertyModifier(token) || token == KindStaticKeyword ||
2780+
token == KindOverrideKeyword || token == KindAccessorKeyword
2781+
}
2782+
2783+
func IsParameterPropertyModifier(kind Kind) bool {
2784+
return ModifierToFlag(kind)&ModifierFlagsParameterPropertyModifier != 0
2785+
}

internal/astnav/tokens.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ func FindPrecedingTokenEx(sourceFile *ast.SourceFile, position int, startNode *a
344344
// 1) `position` precedes `child`'s tokens or `child` has no tokens (ie: in a comment or whitespace preceding `child`):
345345
// we need to find the last token in a previous child node or child tokens.
346346
// 2) `position` is within the same span: we recurse on `child`.
347-
start := getStartOfNode(foundChild, sourceFile, !excludeJSDoc /*includeJSDoc*/)
347+
start := GetStartOfNode(foundChild, sourceFile, !excludeJSDoc /*includeJSDoc*/)
348348
lookInPreviousChild := start >= position || // cursor in the leading trivia or preceding tokens
349349
!isValidPrecedingNode(foundChild, sourceFile)
350350
if lookInPreviousChild {
@@ -398,12 +398,12 @@ func FindPrecedingTokenEx(sourceFile *ast.SourceFile, position int, startNode *a
398398
}
399399

400400
func isValidPrecedingNode(node *ast.Node, sourceFile *ast.SourceFile) bool {
401-
start := getStartOfNode(node, sourceFile, false /*includeJSDoc*/)
401+
start := GetStartOfNode(node, sourceFile, false /*includeJSDoc*/)
402402
width := node.End() - start
403403
return !(ast.IsWhitespaceOnlyJsxText(node) || width == 0)
404404
}
405405

406-
func getStartOfNode(node *ast.Node, file *ast.SourceFile, includeJSDoc bool) int {
406+
func GetStartOfNode(node *ast.Node, file *ast.SourceFile, includeJSDoc bool) int {
407407
return scanner.GetTokenPosOfNode(node, file, includeJSDoc)
408408
}
409409

@@ -433,7 +433,7 @@ func findRightmostValidToken(endPos int, sourceFile *ast.SourceFile, containingN
433433
shouldVisitNode := func(node *ast.Node) bool {
434434
// Node is synthetic or out of the desired range: don't visit it.
435435
return !(node.Flags&ast.NodeFlagsReparsed != 0 ||
436-
node.End() > endPos || getStartOfNode(node, sourceFile, !excludeJSDoc /*includeJSDoc*/) >= position)
436+
node.End() > endPos || GetStartOfNode(node, sourceFile, !excludeJSDoc /*includeJSDoc*/) >= position)
437437
}
438438
visitNode := func(node *ast.Node, _ *ast.NodeVisitor) *ast.Node {
439439
if node == nil {
@@ -560,3 +560,8 @@ func findRightmostValidToken(endPos int, sourceFile *ast.SourceFile, containingN
560560

561561
return find(containingNode)
562562
}
563+
564+
// !!!
565+
func FindNextToken(previousToken *ast.Node, parent *ast.Node, file *ast.SourceFile) *ast.Node {
566+
return nil
567+
}

0 commit comments

Comments
 (0)