1
+ // Copyright 2025 CloudWeGo Authors
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // https://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ package typescript
16
+
17
+ import (
18
+ "fmt"
19
+ "path/filepath"
20
+ "strings"
21
+
22
+ lsp "github.com/cloudwego/abcoder/lang/lsp"
23
+ "github.com/cloudwego/abcoder/lang/uniast"
24
+ )
25
+
26
+ var _ lsp.LanguageSpec = (* TypeScriptSpec )(nil )
27
+
28
+ type TypeScriptSpec struct {
29
+ repo string
30
+ }
31
+
32
+ func NewTypeScriptSpec () * TypeScriptSpec {
33
+ return & TypeScriptSpec {}
34
+ }
35
+
36
+ func (c * TypeScriptSpec ) FileImports (content []byte ) ([]uniast.Import , error ) {
37
+ // TODO: Parse TypeScript import statements
38
+ return []uniast.Import {}, nil
39
+ }
40
+
41
+ func (c * TypeScriptSpec ) IsExternalEntityToken (tok lsp.Token ) bool {
42
+ if ! c .IsEntityToken (tok ) {
43
+ return false
44
+ }
45
+ for _ , m := range tok .Modifiers {
46
+ if m == "defaultLibrary" {
47
+ return true
48
+ }
49
+ }
50
+ return false
51
+ }
52
+
53
+ func (c * TypeScriptSpec ) TokenKind (tok lsp.Token ) lsp.SymbolKind {
54
+ switch tok .Type {
55
+ case "class" :
56
+ return lsp .SKClass
57
+ case "interface" :
58
+ return lsp .SKInterface
59
+ case "function" :
60
+ return lsp .SKFunction
61
+ case "method" :
62
+ return lsp .SKMethod
63
+ case "property" :
64
+ return lsp .SKProperty
65
+ case "variable" :
66
+ return lsp .SKVariable
67
+ case "const" :
68
+ return lsp .SKConstant
69
+ case "enum" :
70
+ return lsp .SKEnum
71
+ case "enumMember" :
72
+ return lsp .SKEnumMember
73
+ case "type" :
74
+ return lsp .SKTypeParameter
75
+ case "namespace" :
76
+ return lsp .SKNamespace
77
+ case "module" :
78
+ return lsp .SKModule
79
+ default :
80
+ return lsp .SKUnknown
81
+ }
82
+ }
83
+
84
+ func (c * TypeScriptSpec ) IsStdToken (tok lsp.Token ) bool {
85
+ for _ , m := range tok .Modifiers {
86
+ if m == "defaultLibrary" {
87
+ return true
88
+ }
89
+ }
90
+ return false
91
+ }
92
+
93
+ func (c * TypeScriptSpec ) IsDocToken (tok lsp.Token ) bool {
94
+ for _ , m := range tok .Modifiers {
95
+ if m == "documentation" {
96
+ return true
97
+ }
98
+ }
99
+ return false
100
+ }
101
+
102
+ func (c * TypeScriptSpec ) DeclareTokenOfSymbol (sym lsp.DocumentSymbol ) int {
103
+ for i , t := range sym .Tokens {
104
+ if c .IsDocToken (t ) {
105
+ continue
106
+ }
107
+ for _ , m := range t .Modifiers {
108
+ if m == "declaration" {
109
+ return i
110
+ }
111
+ }
112
+ }
113
+ return - 1
114
+ }
115
+
116
+ func (c * TypeScriptSpec ) IsPublicSymbol (sym lsp.DocumentSymbol ) bool {
117
+ // In TypeScript, symbols are public by default unless marked private/protected
118
+ id := c .DeclareTokenOfSymbol (sym )
119
+ if id == - 1 {
120
+ return true
121
+ }
122
+ for _ , m := range sym .Tokens [id ].Modifiers {
123
+ if m == "private" || m == "protected" {
124
+ return false
125
+ }
126
+ }
127
+ return true
128
+ }
129
+
130
+ func (c * TypeScriptSpec ) IsMainFunction (sym lsp.DocumentSymbol ) bool {
131
+ // TypeScript doesn't have a main function concept
132
+ return false
133
+ }
134
+
135
+ func (c * TypeScriptSpec ) IsEntitySymbol (sym lsp.DocumentSymbol ) bool {
136
+ typ := sym .Kind
137
+ return typ == lsp .SKClass || typ == lsp .SKMethod || typ == lsp .SKFunction ||
138
+ typ == lsp .SKVariable || typ == lsp .SKInterface || typ == lsp .SKConstant ||
139
+ typ == lsp .SKEnum || typ == lsp .SKTypeParameter || typ == lsp .SKNamespace ||
140
+ typ == lsp .SKModule
141
+ }
142
+
143
+ func (c * TypeScriptSpec ) IsEntityToken (tok lsp.Token ) bool {
144
+ typ := tok .Type
145
+ return typ == "class" || typ == "interface" || typ == "function" ||
146
+ typ == "method" || typ == "property" || typ == "variable" ||
147
+ typ == "const" || typ == "enum" || typ == "enumMember" ||
148
+ typ == "type" || typ == "namespace" || typ == "module"
149
+ }
150
+
151
+ func (c * TypeScriptSpec ) HasImplSymbol () bool {
152
+ // TypeScript uses class/interface implementation, not impl blocks like Rust
153
+ return false
154
+ }
155
+
156
+ func (c * TypeScriptSpec ) ImplSymbol (sym lsp.DocumentSymbol ) (int , int , int ) {
157
+ // TypeScript doesn't have impl blocks
158
+ return - 1 , - 1 , - 1
159
+ }
160
+
161
+ func (c * TypeScriptSpec ) FunctionSymbol (sym lsp.DocumentSymbol ) (int , []int , []int , []int ) {
162
+ // TODO: Implement TypeScript function parsing
163
+ return - 1 , nil , nil , nil
164
+ }
165
+
166
+ func (c * TypeScriptSpec ) ShouldSkip (path string ) bool {
167
+ if strings .Contains (path , "/node_modules/" ) {
168
+ return true
169
+ }
170
+ if ! strings .HasSuffix (path , ".ts" ) && ! strings .HasSuffix (path , ".tsx" ) {
171
+ return true
172
+ }
173
+ return false
174
+ }
175
+
176
+ func (c * TypeScriptSpec ) NameSpace (path string ) (string , string , error ) {
177
+ if ! strings .HasPrefix (path , c .repo ) {
178
+ // External module
179
+ return "" , "" , fmt .Errorf ("external module: %s" , path )
180
+ }
181
+
182
+ // Calculate relative path from repo root
183
+ rel , err := filepath .Rel (c .repo , path )
184
+ if err != nil {
185
+ return "" , "" , err
186
+ }
187
+
188
+ // Remove file extension
189
+ rel = strings .TrimSuffix (rel , ".ts" )
190
+ rel = strings .TrimSuffix (rel , ".tsx" )
191
+
192
+ // Remove index suffix if present
193
+ if strings .HasSuffix (rel , "/index" ) {
194
+ rel = strings .TrimSuffix (rel , "/index" )
195
+ }
196
+
197
+ // Convert path to module name
198
+ module := strings .ReplaceAll (rel , string (filepath .Separator ), "." )
199
+
200
+ return module , module , nil
201
+ }
202
+
203
+ func (c * TypeScriptSpec ) WorkSpace (root string ) (map [string ]string , error ) {
204
+ c .repo = root
205
+ // For TypeScript, we don't need to collect modules like Rust
206
+ // The module system is based on file paths
207
+ return map [string ]string {}, nil
208
+ }
209
+
210
+ func (c * TypeScriptSpec ) GetUnloadedSymbol (from lsp.Token , loc lsp.Location ) (string , error ) {
211
+ // TODO: Implement TypeScript unloaded symbol extraction
212
+ return "" , nil
213
+ }
0 commit comments