Skip to content

Commit 4daab2f

Browse files
committed
feat: support classes for python
1 parent 36711bb commit 4daab2f

2 files changed

Lines changed: 72 additions & 14 deletions

File tree

lang/collect/collect.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,11 +580,13 @@ func (c *Collector) collectImpl(ctx context.Context, sym *DocumentSymbol, depth
580580
}
581581
}
582582
var impl string
583+
// HACK: impl head for Rust.
583584
if fn > 0 && fn < len(sym.Tokens) {
584585
impl = ChunkHead(sym.Text, sym.Location.Range.Start, sym.Tokens[fn].Location.Range.Start)
585586
}
587+
// HACK: implhead for Python. Should actually be provided by the language spec.
586588
if impl == "" || len(impl) < len(sym.Name) {
587-
impl = sym.Name
589+
impl = fmt.Sprintf("class %s {\n", sym.Name)
588590
}
589591
// search all methods
590592
for _, method := range c.syms {

lang/python/spec.go

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -203,24 +203,81 @@ func (c *PythonSpec) IsEntitySymbol(sym lsp.DocumentSymbol) bool {
203203
}
204204

205205
func (c *PythonSpec) IsPublicSymbol(sym lsp.DocumentSymbol) bool {
206+
// builtin methods are exported
207+
if strings.HasPrefix(sym.Name, "__") && strings.HasSuffix(sym.Name, "__") {
208+
return true
209+
}
206210
if strings.HasPrefix(sym.Name, "_") {
207211
return false
208212
}
209213
return true
210214
}
211215

212216
func (c *PythonSpec) HasImplSymbol() bool {
213-
// Python does not have direct impl symbols
214-
return false
217+
return true
215218
}
216219

220+
func invalidPos() lsp.Position {
221+
return lsp.Position{
222+
Line: -1,
223+
Character: -1,
224+
}
225+
}
226+
227+
// returns interface, receiver, first method
217228
func (c *PythonSpec) ImplSymbol(sym lsp.DocumentSymbol) (int, int, int) {
218-
panic("TODO")
229+
// reference: https://docs.python.org/3/reference/grammar.html
230+
if sym.Kind != lsp.SKClass {
231+
return -1, -1, -1
232+
}
233+
234+
implType := -1
235+
receiverType := -1
236+
firstMethod := -1
237+
238+
// state 0: goto state -1 when we see a 'class'
239+
state := 0
240+
clsnamepos := invalidPos()
241+
curpos := sym.Location.Range.Start
242+
for i := range len(sym.Text) {
243+
if state == -1 {
244+
break
245+
}
246+
switch state {
247+
case 0:
248+
if i+6 >= len(sym.Text) {
249+
// class text does not contain a 'class'
250+
// should be an import
251+
return -1, -1, -1
252+
}
253+
next6chars := sym.Text[i : i+6]
254+
// heuristics should work with reasonable python code
255+
if next6chars == "class " {
256+
clsnamepos = curpos
257+
state = -1
258+
}
259+
}
260+
if sym.Text[i] == '\n' {
261+
curpos.Line++
262+
curpos.Character = 0
263+
} else {
264+
curpos.Character++
265+
}
266+
}
267+
268+
for i, t := range sym.Tokens {
269+
if receiverType == -1 && clsnamepos.Less(t.Location.Range.Start) {
270+
receiverType = i
271+
}
272+
}
273+
274+
return implType, receiverType, firstMethod
219275
}
220276

221277
// returns: receiver, typeParams, inputParams, outputParams
222278
func (c *PythonSpec) FunctionSymbol(sym lsp.DocumentSymbol) (int, []int, []int, []int) {
223-
// no receiver. no type params in python
279+
// FunctionSymbol do not return receivers.
280+
// TODO type params in python (nobody uses them)
224281
// reference: https://docs.python.org/3/reference/grammar.html
225282
receiver := -1
226283
// python actually has these but TODO
@@ -237,20 +294,19 @@ func (c *PythonSpec) FunctionSymbol(sym lsp.DocumentSymbol) (int, []int, []int,
237294
// finish when we see a :
238295
state := 0
239296
paren_depth := 0
240-
invalidpos := lsp.Position{
241-
Line: -1,
242-
Character: -1,
243-
}
244-
// defpos := invalidpos
245-
lparenpos := invalidpos
246-
rparenpos := invalidpos
247-
bodypos := invalidpos
297+
// defpos := invalidPos()
298+
lparenpos := invalidPos()
299+
rparenpos := invalidPos()
300+
bodypos := invalidPos()
248301
curpos := sym.Location.Range.Start
249302
for i := range len(sym.Text) {
303+
if state == -1 {
304+
break
305+
}
250306
switch state {
251307
case 0:
252308
if i+4 >= len(sym.Text) {
253-
// function text does not contain a def
309+
// function text does not contain a 'def'
254310
// should be an import
255311
return -1, []int{}, []int{}, []int{}
256312
}

0 commit comments

Comments
 (0)