blob: a0e1ba86636fa771be4ef2fd60b5a2354b21b0f9 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package langserver
import (
"context"
"fmt"
"path/filepath"
"github.com/sourcegraph/go-lsp"
"github.com/sourcegraph/jsonrpc2"
"fidl-lsp/analysis"
)
// clientConfig are the configuration settings from the LSP client. These are
// relevant to the Analyzer. If they are left unset, the Analyzer uses its
// defaults.
type clientConfig struct {
FidlProject string `json:"fidlProject,omitempty"`
BuildRoot string `json:"projectRoot,omitempty"`
Tools toolsConfig `json:"tools,omitempty"`
}
type toolsConfig struct {
Compiler string `json:"compiler,omitempty"`
Linter string `json:"linter,omitempty"`
Formatter string `json:"formatter,omitempty"`
}
func (h *LangHandler) requestConfiguration(ctx context.Context, conn *jsonrpc2.Conn) {
go func() {
var cfg []clientConfig
conn.Call(
ctx,
"workspace/configuration",
lsp.ConfigurationParams{
Items: []lsp.ConfigurationItem{{Section: "fidl"}},
},
&cfg,
)
if len(cfg) < 1 {
h.log.Printf("received invalid configuration settings. unable to update\n")
// Try loading the default fidl_project.json path
h.loadFidlProject(ctx, conn, h.cfg.fidlProjectPath)
return
}
h.updateConfig(ctx, conn, cfg[0])
}()
}
func (h *LangHandler) handleDidChangeConfiguration(ctx context.Context, conn *jsonrpc2.Conn, params lsp.DidChangeConfigurationParams) {
h.log.Printf("updating with new config settings: %#v", params.Settings)
cfg, ok := params.Settings.([]clientConfig)
if !ok {
h.log.Printf("received invalid configuration settings. unable to update\n")
return
}
if len(cfg) < 1 {
h.log.Printf("received invalid configuration settings. unable to update\n")
return
}
h.updateConfig(ctx, conn, cfg[0])
}
func (h *LangHandler) updateConfig(ctx context.Context, conn *jsonrpc2.Conn, cfg clientConfig) {
if cfg.FidlProject == "" {
// FidlProject is unset; try loading the default fidl_project.json path
h.loadFidlProject(ctx, conn, h.cfg.fidlProjectPath)
} else {
h.loadFidlProject(ctx, conn, cfg.FidlProject)
// If the BuildRoot is unset, set it to the directory containing
// `FidlProject`
if cfg.BuildRoot == "" {
cfg.BuildRoot = filepath.Dir(cfg.FidlProject)
}
}
h.analyzer.SetConfig(analysis.Config{
BuildRootDir: cfg.BuildRoot,
FidlcPath: cfg.Tools.Compiler,
FidlLintPath: cfg.Tools.Linter,
FidlFormatPath: cfg.Tools.Formatter,
})
// Re-analyze files that could have been affected by the new config
for _, file := range h.fs.Files() {
if err := h.analyzer.Analyze(h.fs, file); err != nil {
h.log.Println(err)
}
h.publishDiagnostics(ctx, conn, lsp.DocumentURI(file))
}
}
// loadFidlProject attempts to read in the fidl_project.json file at
// `fidlProjectPath`, and if it can't, it displays an error dialog in the client
// so the user can try to regenerate the file.
func (h *LangHandler) loadFidlProject(ctx context.Context, conn *jsonrpc2.Conn, fidlProjectPath string) {
fidlProject, err := analysis.LoadFidlProject(fidlProjectPath)
if err != nil {
h.log.Printf("failed to parse fidl_project.json at `%s`: %s\n", fidlProjectPath, err)
h.Notify(ctx, conn, "window/showMessage", lsp.ShowMessageParams{
Type: lsp.MTError,
Message: fmt.Sprintf(
"Failed to load fidl_project.json at '%s'.\n"+
"To generate this file, run "+
"`fx gen && fx build build/fidl:validate_fidl_project_json`. "+
"Alternatively, custom path can be specified in settings.",
fidlProjectPath,
),
})
}
h.analyzer.ImportCompiledLibraries(fidlProject)
}