Preserve Gotlin semantics across packages
This commit is contained in:
parent
5b30e49486
commit
acb42a5702
10 changed files with 525 additions and 34 deletions
|
|
@ -326,27 +326,43 @@ func (s *server) buildDocumentState(uri, text string) documentState {
|
|||
return state
|
||||
}
|
||||
state.program = program
|
||||
programs, symbols := s.packageContext(uri, program.PackagePath, text)
|
||||
_, semanticDiagnostics := lang.AnalyzeWithContext(program, programs)
|
||||
programs, symbols, metadata := s.packageContext(uri, program.PackagePath, text)
|
||||
_, semanticDiagnostics := lang.AnalyzeWithContextAndMetadata(program, programs, metadata)
|
||||
state.symbols = indexSymbols(text, program)
|
||||
state.packageSymbols = symbols
|
||||
state.diagnostics = diagnosticsFromSemantic(text, semanticDiagnostics)
|
||||
return state
|
||||
}
|
||||
|
||||
func (s *server) packageContext(currentURI, packagePath, currentText string) ([]*lang.Program, []symbol) {
|
||||
func (s *server) packageContext(currentURI, packagePath, currentText string) ([]*lang.Program, []symbol, []*lang.PackageMetadata) {
|
||||
path, ok := filePathFromURI(currentURI)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
root := nearestWorkspaceRoot(filepath.Dir(path))
|
||||
if root == "" {
|
||||
root = nearestModuleRoot(filepath.Dir(path))
|
||||
}
|
||||
root := nearestModuleRoot(filepath.Dir(path))
|
||||
if root == "" {
|
||||
root = filepath.Dir(path)
|
||||
}
|
||||
var programs []*lang.Program
|
||||
var symbols []symbol
|
||||
var metadata []*lang.PackageMetadata
|
||||
_ = filepath.WalkDir(root, func(candidate string, entry os.DirEntry, err error) error {
|
||||
if err != nil || entry.IsDir() || filepath.Ext(candidate) != ".gt" {
|
||||
if err != nil || entry.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if strings.HasSuffix(candidate, ".gti.json") {
|
||||
if body, readErr := os.ReadFile(candidate); readErr == nil {
|
||||
var item lang.PackageMetadata
|
||||
if json.Unmarshal(body, &item) == nil && item.Version == lang.PackageMetadataVersion {
|
||||
metadata = append(metadata, &item)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if filepath.Ext(candidate) != ".gt" {
|
||||
return nil
|
||||
}
|
||||
candidateURI := fileURI(candidate)
|
||||
|
|
@ -374,7 +390,20 @@ func (s *server) packageContext(currentURI, packagePath, currentText string) ([]
|
|||
}
|
||||
return nil
|
||||
})
|
||||
return programs, symbols
|
||||
return programs, symbols, metadata
|
||||
}
|
||||
|
||||
func nearestWorkspaceRoot(directory string) string {
|
||||
for {
|
||||
if _, err := os.Stat(filepath.Join(directory, "go.work")); err == nil {
|
||||
return directory
|
||||
}
|
||||
parent := filepath.Dir(directory)
|
||||
if parent == directory {
|
||||
return ""
|
||||
}
|
||||
directory = parent
|
||||
}
|
||||
}
|
||||
|
||||
func nearestModuleRoot(directory string) string {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
|
@ -32,6 +33,10 @@ func runBuild(args []string) {
|
|||
fs := flag.NewFlagSet("build", flag.ExitOnError)
|
||||
srcOnly := fs.Bool("src", false, "emit Go source instead of building an executable")
|
||||
outPath := fs.String("o", "", "output file path")
|
||||
metadataOutput := fs.String("metadata-output", "", "write Gotlin package metadata")
|
||||
metadataPackage := fs.String("metadata-package", "", "import path recorded in package metadata")
|
||||
var metadataInputs stringListFlag
|
||||
fs.Var(&metadataInputs, "metadata", "Gotlin package metadata dependency (repeatable)")
|
||||
fs.Usage = func() {
|
||||
fmt.Fprintln(os.Stderr, "usage: gotlinc build [-src] [-o output] <input.gt>")
|
||||
}
|
||||
|
|
@ -48,7 +53,7 @@ func runBuild(args []string) {
|
|||
}
|
||||
|
||||
inputPath := fs.Arg(0)
|
||||
goSrc := compileFiles(fs.Args(), !*srcOnly)
|
||||
goSrc := compileFilesWithMetadata(fs.Args(), !*srcOnly, metadataInputs, *metadataOutput, *metadataPackage)
|
||||
|
||||
if *srcOnly {
|
||||
if *outPath == "" {
|
||||
|
|
@ -117,6 +122,10 @@ func compileFile(inputPath string, forceMain bool) string {
|
|||
}
|
||||
|
||||
func compileFiles(inputPaths []string, forceMain bool) string {
|
||||
return compileFilesWithMetadata(inputPaths, forceMain, nil, "", "")
|
||||
}
|
||||
|
||||
func compileFilesWithMetadata(inputPaths []string, forceMain bool, metadataPaths []string, metadataOutput, metadataPackage string) string {
|
||||
program := &lang.Program{}
|
||||
var firstSource string
|
||||
for _, inputPath := range inputPaths {
|
||||
|
|
@ -146,12 +155,27 @@ func compileFiles(inputPaths []string, forceMain bool) string {
|
|||
firstSource = string(src)
|
||||
}
|
||||
}
|
||||
metadata := loadMetadata(metadataPaths)
|
||||
if metadataOutput != "" {
|
||||
packageMetadata, err := lang.BuildPackageMetadata(program, metadataPackage)
|
||||
if err != nil {
|
||||
fail(err)
|
||||
}
|
||||
encoded, err := json.MarshalIndent(packageMetadata, "", " ")
|
||||
if err != nil {
|
||||
fail(err)
|
||||
}
|
||||
encoded = append(encoded, '\n')
|
||||
if err := os.WriteFile(metadataOutput, encoded, 0o644); err != nil {
|
||||
fail(err)
|
||||
}
|
||||
}
|
||||
var goSrc []byte
|
||||
var err error
|
||||
if forceMain {
|
||||
goSrc, err = lang.GenerateGoMain(program)
|
||||
goSrc, err = lang.GenerateGoMainWithMetadata(program, metadata)
|
||||
} else {
|
||||
goSrc, err = lang.GenerateGo(program)
|
||||
goSrc, err = lang.GenerateGoWithMetadata(program, metadata)
|
||||
}
|
||||
if err != nil {
|
||||
fail(err)
|
||||
|
|
@ -162,6 +186,30 @@ func compileFiles(inputPaths []string, forceMain bool) string {
|
|||
return string(goSrc)
|
||||
}
|
||||
|
||||
type stringListFlag []string
|
||||
|
||||
func (values *stringListFlag) String() string { return strings.Join(*values, ",") }
|
||||
func (values *stringListFlag) Set(value string) error { *values = append(*values, value); return nil }
|
||||
|
||||
func loadMetadata(paths []string) []*lang.PackageMetadata {
|
||||
metadata := make([]*lang.PackageMetadata, 0, len(paths))
|
||||
for _, path := range paths {
|
||||
body, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
fail(err)
|
||||
}
|
||||
var item lang.PackageMetadata
|
||||
if err := json.Unmarshal(body, &item); err != nil {
|
||||
fail(fmt.Errorf("load metadata %s: %w", path, err))
|
||||
}
|
||||
if item.Version != lang.PackageMetadataVersion {
|
||||
fail(fmt.Errorf("metadata %s uses unsupported version %d", path, item.Version))
|
||||
}
|
||||
metadata = append(metadata, &item)
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
func buildExecutable(goSrc string, outputPath string) error {
|
||||
tmpFile, err := os.CreateTemp("", "gotlinc-build-*.go")
|
||||
if err != nil {
|
||||
|
|
@ -202,7 +250,7 @@ func normalizeBuildArgs(args []string) ([]string, error) {
|
|||
switch {
|
||||
case arg == "-src":
|
||||
flags = append(flags, arg)
|
||||
case arg == "-o":
|
||||
case arg == "-o" || arg == "-metadata" || arg == "-metadata-output" || arg == "-metadata-package":
|
||||
if i+1 >= len(args) {
|
||||
return nil, fmt.Errorf("missing value for -o")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue