@@ -18,6 +18,7 @@ import (
1818 "fmt"
1919 "os"
2020 "path/filepath"
21+ "regexp"
2122 "strings"
2223
2324 lsp "github.com/cloudwego/abcoder/lang/lsp"
@@ -147,12 +148,6 @@ func (c *PythonSpec) DeclareTokenOfSymbol(sym lsp.DocumentSymbol) int {
147148
148149func (c * PythonSpec ) IsEntityToken (tok lsp.Token ) bool {
149150 typ := tok .Type
150- if strings .HasPrefix (tok .Text , "from " ) || strings .HasPrefix (tok .Text , "import " ) {
151- // Python LSP highlights imported symbols as function/types
152- // We decide that imported symbols are not entities.
153- // In fact, they ARE, just in a different place.
154- return false
155- }
156151 return typ == "function" || typ == "variable" || typ == "property" || typ == "class" || typ == "type"
157152}
158153
@@ -209,10 +204,6 @@ func (c *PythonSpec) IsMainFunction(sym lsp.DocumentSymbol) bool {
209204}
210205
211206func (c * PythonSpec ) IsEntitySymbol (sym lsp.DocumentSymbol ) bool {
212- // Same as in IsEntityToken, we do not consider imported symbols as entities.
213- if strings .HasPrefix (sym .Text , "from " ) || strings .HasPrefix (sym .Text , "import " ) {
214- return false
215- }
216207 typ := sym .Kind
217208 return typ == lsp .SKObject || typ == lsp .SKMethod || typ == lsp .SKFunction || typ == lsp .SKVariable ||
218209 typ == lsp .SKStruct || typ == lsp .SKEnum || typ == lsp .SKTypeParameter || typ == lsp .SKConstant || typ == lsp .SKClass
@@ -390,7 +381,39 @@ func (c *PythonSpec) GetUnloadedSymbol(from lsp.Token, define lsp.Location) (str
390381 panic ("TODO" )
391382}
392383
393- // TODO!
394384func (c * PythonSpec ) FileImports (content []byte ) ([]uniast.Import , error ) {
395- return nil , nil
385+ // Reference:
386+ // https://docs.python.org/3/reference/grammar.html
387+ // There are two types of imports in Python:
388+ // import-as: on ONE line
389+ // import xxx as x, yyy as y
390+ // from-import: on ONE line
391+ // from ... import *
392+ // from ... import xxx as x, yyy as y
393+ // or on POSSIBLY MULTIPLE lines, enclosed by parentheses
394+ // from ... import ( xxx, yyy as y ... )
395+ // And imports are simple stmts, so they MUST end with \n.
396+ patterns := []string {
397+ // Matches: import <anything> (on a single line)
398+ `(?m)^import\s+(.*)$` ,
399+ // Matches: from <anything> import <anything> (on a single line, without parentheses)
400+ `(?m)^from\s+(.*?)\s+import\s+([^()\n]*)$` ,
401+ // Matches: from <anything> import ( <anything> ) where <anything> can span multiple lines
402+ `(?m)^from\s+(.*?)\s+import\s+\(([\s\S]*?)\)$` ,
403+ }
404+
405+ res := []uniast.Import {}
406+ for _ , p := range patterns {
407+ re , err := regexp .Compile (p )
408+ if err != nil {
409+ return nil , fmt .Errorf ("error compiling regex pattern '%s': %w" , p , err )
410+ }
411+ matches := re .FindAllStringSubmatch (string (content ), - 1 ) // -1 to find all non-overlapping matches
412+ for _ , match := range matches {
413+ res = append (res , uniast.Import {
414+ Path : match [0 ],
415+ })
416+ }
417+ }
418+ return res , nil
396419}
0 commit comments