github.com/goplus/gop@v1.2.6/outline.go (about)

     1  /*
     2   * Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package gop
    18  
    19  import (
    20  	"fmt"
    21  	"go/token"
    22  	"io/fs"
    23  	"os"
    24  	"path"
    25  	"path/filepath"
    26  	"strings"
    27  
    28  	"github.com/goplus/gop/cl/outline"
    29  	"github.com/goplus/gop/parser"
    30  	"github.com/goplus/gop/x/c2go"
    31  	"github.com/goplus/gop/x/gopenv"
    32  	"github.com/goplus/mod/gopmod"
    33  	"github.com/qiniu/x/errors"
    34  )
    35  
    36  // -----------------------------------------------------------------------------
    37  
    38  func Outline(dir string, conf *Config) (out outline.Package, err error) {
    39  	if dir, err = filepath.Abs(dir); err != nil {
    40  		return
    41  	}
    42  
    43  	if conf == nil {
    44  		conf = new(Config)
    45  	}
    46  
    47  	mod := conf.Mod
    48  	if mod == nil {
    49  		if mod, err = LoadMod(dir); err != nil {
    50  			err = errors.NewWith(err, `LoadMod(dir)`, -2, "gop.LoadMod", dir)
    51  			return
    52  		}
    53  	}
    54  
    55  	filterConf := conf.Filter
    56  	filter := func(fi fs.FileInfo) bool {
    57  		if filterConf != nil && !filterConf(fi) {
    58  			return false
    59  		}
    60  		fname := fi.Name()
    61  		if pos := strings.Index(fname, "."); pos > 0 {
    62  			fname = fname[:pos]
    63  		}
    64  		return !strings.HasSuffix(fname, "_test")
    65  	}
    66  	fset := conf.Fset
    67  	if fset == nil {
    68  		fset = token.NewFileSet()
    69  	}
    70  	pkgs, err := parser.ParseDirEx(fset, dir, parser.Config{
    71  		ClassKind: mod.ClassKind,
    72  		Filter:    filter,
    73  		Mode:      parser.ParseComments,
    74  	})
    75  	if err != nil {
    76  		return
    77  	}
    78  	if len(pkgs) == 0 {
    79  		err = ErrNotFound
    80  		return
    81  	}
    82  
    83  	imp := conf.Importer
    84  	if imp == nil {
    85  		gop := conf.Gop
    86  		if gop == nil {
    87  			gop = gopenv.Get()
    88  		}
    89  		imp = NewImporter(mod, gop, fset)
    90  	}
    91  
    92  	for name, pkg := range pkgs {
    93  		if out.Valid() {
    94  			err = fmt.Errorf("%w: %s, %s", ErrMultiPackges, name, out.Pkg().Name())
    95  			return
    96  		}
    97  		if len(pkg.Files)+len(pkg.GoFiles) == 0 { // no Go/Go+ source files
    98  			break
    99  		}
   100  		relPart, _ := filepath.Rel(mod.Root(), dir)
   101  		pkgPath := path.Join(mod.Path(), filepath.ToSlash(relPart))
   102  		out, err = outline.NewPackage(pkgPath, pkg, &outline.Config{
   103  			Fset:        fset,
   104  			Importer:    imp,
   105  			LookupClass: mod.LookupClass,
   106  			LookupPub:   c2go.LookupPub(mod),
   107  		})
   108  		if err != nil {
   109  			return
   110  		}
   111  	}
   112  	if !out.Valid() {
   113  		err = ErrNotFound
   114  	}
   115  	return
   116  }
   117  
   118  // -----------------------------------------------------------------------------
   119  
   120  func OutlinePkgPath(workDir, pkgPath string, conf *Config, allowExtern bool) (out outline.Package, err error) {
   121  	mod := conf.Mod
   122  	if mod == nil {
   123  		if mod, err = LoadMod(workDir); err != nil {
   124  			err = errors.NewWith(err, `LoadMod(dir)`, -2, "gop.LoadMod", workDir)
   125  			return
   126  		}
   127  	}
   128  
   129  	if NotFound(err) && allowExtern {
   130  		remotePkgPathDo(pkgPath, func(pkgDir, modDir string) {
   131  			modFile := chmodModfile(modDir)
   132  			defer os.Chmod(modFile, modReadonly)
   133  			out, err = Outline(pkgDir, conf)
   134  		}, func(e error) {
   135  			err = e
   136  		})
   137  		return
   138  	} else if err != nil {
   139  		return
   140  	}
   141  
   142  	pkg, err := mod.Lookup(pkgPath)
   143  	if err != nil {
   144  		return
   145  	}
   146  	if pkg.Type == gopmod.PkgtExtern {
   147  		modFile := chmodModfile(pkg.ModDir)
   148  		defer os.Chmod(modFile, modReadonly)
   149  	}
   150  	return Outline(pkg.Dir, conf)
   151  }
   152  
   153  func chmodModfile(modDir string) string {
   154  	modFile := modDir + "/go.mod"
   155  	os.Chmod(modFile, modWritable)
   156  	return modFile
   157  }
   158  
   159  // -----------------------------------------------------------------------------