Skip to content

Commit f5b817d

Browse files
committed
feat: support ts
1 parent 70c2e1c commit f5b817d

File tree

3 files changed

+258
-0
lines changed

3 files changed

+258
-0
lines changed

lang/parse.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/cloudwego/abcoder/lang/lsp"
3333
"github.com/cloudwego/abcoder/lang/python"
3434
"github.com/cloudwego/abcoder/lang/rust"
35+
"github.com/cloudwego/abcoder/lang/typescript"
3536
"github.com/cloudwego/abcoder/lang/uniast"
3637
)
3738

@@ -120,6 +121,8 @@ func checkRepoPath(repoPath string, language uniast.Language) (openfile string,
120121
openfile, wait = cxx.CheckRepo(repoPath)
121122
case uniast.Python:
122123
openfile, wait = python.CheckRepo(repoPath)
124+
case uniast.TypeScript:
125+
openfile, wait = typescript.CheckRepo(repoPath)
123126
default:
124127
openfile = ""
125128
wait = 0
@@ -137,6 +140,8 @@ func checkLSP(language uniast.Language, lspPath string) (l uniast.Language, s st
137140
l, s = cxx.GetDefaultLSP()
138141
case uniast.Python:
139142
l, s = python.GetDefaultLSP()
143+
case uniast.TypeScript:
144+
l, s = typescript.GetDefaultLSP()
140145
case uniast.Golang:
141146
l = uniast.Golang
142147
s = ""

lang/typescript/lib.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
"time"
19+
20+
"github.com/cloudwego/abcoder/lang/uniast"
21+
"github.com/cloudwego/abcoder/lang/utils"
22+
)
23+
24+
const MaxWaitDuration = 5 * time.Second
25+
26+
func GetDefaultLSP() (lang uniast.Language, name string) {
27+
return uniast.TypeScript, "typescript-language-server"
28+
}
29+
30+
func CheckRepo(repo string) (string, time.Duration) {
31+
openfile := ""
32+
33+
// Give the LSP sometime to initialize
34+
_, size := utils.CountFiles(repo, ".ts", "node_modules/")
35+
wait := 2*time.Second + time.Second*time.Duration(size/1024)
36+
if wait > MaxWaitDuration {
37+
wait = MaxWaitDuration
38+
}
39+
return openfile, wait
40+
}

lang/typescript/spec.go

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
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

Comments
 (0)