Skip to content

Commit a9580aa

Browse files
committed
Big refactoring in LSP handling
This is actually much more like a rewriting of the engine than a refactoring. Really too much stuff going on to keep track of them cleanly, a (possibly) short recap may be: - before starting clangd we make a round of arduino-preprocessing - using the new arduino-cli --only-compilation-database flag to generate both compiler flags, preprocessed sketch, etc. - multiple .ino files are now mapped into a single .cpp file - added support for clangd -query-driver flag to query cross-compiler native libraries and includes directories - using type-switch to handle messages in-out from IDE For now the LSP command partially implemented are: 'initialize' 'textDocument/didOpen'
1 parent a01cf52 commit a9580aa

File tree

7 files changed

+616
-314
lines changed

7 files changed

+616
-314
lines changed

Diff for: go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ module github.com/bcmi-labs/arduino-language-server
22

33
go 1.12
44

5+
replace github.com/arduino/arduino-cli => ../arduino-cli
6+
57
require (
8+
github.com/arduino/arduino-cli v0.0.0-20201118111649-5edef82c60fb
69
github.com/arduino/go-paths-helper v1.3.2
710
github.com/arduino/go-properties-orderedmap v1.4.0
811
github.com/davecgh/go-spew v1.1.1 // indirect

Diff for: go.sum

+289
Large diffs are not rendered by default.

Diff for: handler/builder.go

+72-157
Original file line numberDiff line numberDiff line change
@@ -3,188 +3,103 @@ package handler
33
import (
44
"bufio"
55
"bytes"
6+
"encoding/json"
67
"io/ioutil"
78
"log"
89
"os"
910
"os/exec"
1011
"path/filepath"
1112
"strings"
1213

14+
"github.com/arduino/arduino-cli/arduino/libraries"
15+
"github.com/arduino/arduino-cli/executils"
16+
"github.com/arduino/go-paths-helper"
1317
"github.com/arduino/go-properties-orderedmap"
1418
"github.com/pkg/errors"
1519
)
1620

17-
func generateCpp(inoCode []byte, sourcePath, fqbn string) (cppPath string, cppCode []byte, err error) {
18-
// The CLI expects the `theSketchName.ino` file to be in `some/path/theSketchName` folder.
19-
// Expected folder structure: `/path/to/temp/ino2cpp-${random}/theSketchName/theSketchName.ino`.
20-
rawRootTempDir, err := ioutil.TempDir("", "ino2cpp-")
21-
if err != nil {
22-
err = errors.Wrap(err, "Error while creating temporary directory.")
23-
return
24-
}
25-
rootTempDir, err := filepath.EvalSymlinks(rawRootTempDir)
26-
if err != nil {
27-
err = errors.Wrap(err, "Error while resolving symbolic links of temporary directory.")
28-
return
29-
}
30-
31-
sketchName := filepath.Base(sourcePath)
32-
if strings.HasSuffix(sketchName, ".ino") {
33-
sketchName = sketchName[:len(sketchName)-len(".ino")]
34-
}
35-
sketchTempPath := filepath.Join(rootTempDir, sketchName)
36-
createDirIfNotExist(sketchTempPath)
37-
38-
// Write source file to temp dir
39-
sketchFileName := sketchName + ".ino"
40-
inoPath := filepath.Join(sketchTempPath, sketchFileName)
41-
err = ioutil.WriteFile(inoPath, inoCode, 0600)
42-
if err != nil {
43-
err = errors.Wrap(err, "Error while writing source file to temporary directory.")
44-
return
45-
}
46-
if enableLogging {
47-
log.Println("Source file written to", inoPath)
48-
}
49-
50-
// Copy all header files to temp dir
51-
err = copyHeaderFiles(filepath.Dir(sourcePath), rootTempDir)
52-
if err != nil {
53-
return
54-
}
55-
56-
// Generate compile_flags.txt
57-
cppPath = filepath.Join(sketchTempPath, sketchFileName+".cpp")
58-
flagsPath, err := generateCompileFlags(sketchTempPath, inoPath, sourcePath, fqbn)
59-
if err != nil {
60-
return
61-
}
62-
if enableLogging {
63-
log.Println("Compile flags written to", flagsPath)
64-
}
65-
66-
// Generate target file
67-
cppCode, err = generateTargetFile(sketchTempPath, inoPath, cppPath, fqbn)
68-
return
69-
}
70-
71-
func createDirIfNotExist(dir string) {
72-
if _, err := os.Stat(dir); os.IsNotExist(err) {
73-
err = os.MkdirAll(dir, os.ModePerm)
74-
if err != nil {
75-
panic(err)
76-
}
77-
}
78-
}
79-
80-
func copyHeaderFiles(sourceDir string, destDir string) error {
81-
fileInfos, err := ioutil.ReadDir(sourceDir)
82-
if err != nil {
83-
return err
84-
}
85-
for _, fileInfo := range fileInfos {
86-
if !fileInfo.IsDir() && strings.HasSuffix(fileInfo.Name(), ".h") {
87-
input, err := ioutil.ReadFile(filepath.Join(sourceDir, fileInfo.Name()))
88-
if err != nil {
89-
return err
90-
}
91-
92-
err = ioutil.WriteFile(filepath.Join(destDir, fileInfo.Name()), input, 0644)
93-
if err != nil {
94-
return err
95-
}
96-
}
97-
}
98-
return nil
99-
}
21+
// func generateCpp(sourcePath, fqbn string) (*paths.Path, []byte, error) {
22+
// // Generate target file
23+
// if cppPath, err := generateBuildEnvironment(paths.New(sourcePath), fqbn); err != nil {
24+
// return nil, nil, err
25+
// } else if cppCode, err := cppPath.ReadFile(); err != nil {
26+
// return nil, nil, err
27+
// } else {
28+
// return cppPath, cppCode, err
29+
// }
30+
// }
10031

10132
func updateCpp(inoCode []byte, sourcePath, fqbn string, fqbnChanged bool, cppPath string) (cppCode []byte, err error) {
102-
tempDir := filepath.Dir(cppPath)
103-
inoPath := strings.TrimSuffix(cppPath, ".cpp")
104-
if inoCode != nil {
105-
// Write source file to temp dir
106-
err = ioutil.WriteFile(inoPath, inoCode, 0600)
107-
if err != nil {
108-
err = errors.Wrap(err, "Error while writing source file to temporary directory.")
109-
return
110-
}
111-
if enableLogging {
112-
log.Println("Source file written to", inoPath)
113-
}
114-
}
33+
// tempDir := filepath.Dir(cppPath)
34+
// inoPath := strings.TrimSuffix(cppPath, ".cpp")
35+
// if inoCode != nil {
36+
// // Write source file to temp dir
37+
// err = ioutil.WriteFile(inoPath, inoCode, 0600)
38+
// if err != nil {
39+
// err = errors.Wrap(err, "Error while writing source file to temporary directory.")
40+
// return
41+
// }
42+
// if enableLogging {
43+
// log.Println("Source file written to", inoPath)
44+
// }
45+
// }
11546

116-
if fqbnChanged {
117-
// Generate compile_flags.txt
118-
var flagsPath string
119-
flagsPath, err = generateCompileFlags(tempDir, inoPath, sourcePath, fqbn)
120-
if err != nil {
121-
return
122-
}
123-
if enableLogging {
124-
log.Println("Compile flags written to", flagsPath)
125-
}
126-
}
47+
// if fqbnChanged {
48+
// // Generate compile_flags.txt
49+
// var flagsPath string
50+
// flagsPath, err = generateCompileFlags(tempDir, inoPath, sourcePath, fqbn)
51+
// if err != nil {
52+
// return
53+
// }
54+
// if enableLogging {
55+
// log.Println("Compile flags written to", flagsPath)
56+
// }
57+
// }
12758

128-
// Generate target file
129-
cppCode, err = generateTargetFile(tempDir, inoPath, cppPath, fqbn)
59+
// // Generate target file
60+
// cppCode, err = generateTargetFile(tempDir, inoPath, cppPath, fqbn)
13061
return
13162
}
13263

133-
func generateCompileFlags(tempDir, inoPath, sourcePath, fqbn string) (string, error) {
134-
var cliArgs []string
135-
if len(fqbn) > 0 {
136-
cliArgs = []string{"compile", "--fqbn", fqbn, "--show-properties", inoPath}
137-
} else {
138-
cliArgs = []string{"compile", "--show-properties", inoPath}
139-
}
140-
propertiesCmd := exec.Command(globalCliPath, cliArgs...)
141-
output, err := propertiesCmd.Output()
142-
if err != nil {
143-
err = logCommandErr(propertiesCmd, output, err, errMsgFilter(tempDir))
144-
return "", err
145-
}
146-
buildProps, err := properties.LoadFromBytes(output)
64+
func generateBuildEnvironment(sketchDir *paths.Path, fqbn string) (*paths.Path, error) {
65+
// XXX: do this from IDE or via gRPC
66+
args := []string{globalCliPath,
67+
"compile",
68+
"--fqbn", fqbn,
69+
"--only-compilation-database",
70+
"--clean",
71+
"--format", "json",
72+
sketchDir.String(),
73+
}
74+
cmd, err := executils.NewProcess(args...)
14775
if err != nil {
148-
return "", errors.Wrap(err, "Error while reading build properties.")
76+
return nil, errors.Errorf("running %s: %s", strings.Join(args, " "), err)
14977
}
150-
flagsPath := filepath.Join(tempDir, "compile_flags.txt")
151-
outFile, err := os.OpenFile(flagsPath, os.O_WRONLY|os.O_CREATE, 0600)
152-
if err != nil {
153-
return flagsPath, errors.Wrap(err, "Error while creating output file for compile flags.")
78+
cmdOutput := &bytes.Buffer{}
79+
cmd.RedirectStdoutTo(cmdOutput)
80+
cmd.SetDirFromPath(sketchDir)
81+
log.Println("running: ", strings.Join(args, " "))
82+
if err := cmd.Run(); err != nil {
83+
return nil, errors.Errorf("running %s: %s", strings.Join(args, " "), err)
15484
}
155-
defer outFile.Close()
15685

157-
printer := Printer{Writer: bufio.NewWriter(outFile)}
158-
printCompileFlags(buildProps, &printer, fqbn)
159-
printLibraryPaths(sourcePath, &printer)
160-
printer.Flush()
161-
return flagsPath, printer.Err
162-
}
163-
164-
func generateTargetFile(tempDir, inoPath, cppPath, fqbn string) (cppCode []byte, err error) {
165-
var cliArgs []string
166-
if len(fqbn) > 0 {
167-
cliArgs = []string{"compile", "--fqbn", fqbn, "--preprocess", inoPath}
168-
} else {
169-
cliArgs = []string{"compile", "--preprocess", inoPath}
86+
type cmdBuilderRes struct {
87+
BuildPath *paths.Path `json:"build_path"`
88+
UsedLibraries []*libraries.Library
17089
}
171-
preprocessCmd := exec.Command(globalCliPath, cliArgs...)
172-
cppCode, err = preprocessCmd.Output()
173-
if err != nil {
174-
err = logCommandErr(preprocessCmd, cppCode, err, errMsgFilter(tempDir))
175-
return
90+
type cmdRes struct {
91+
CompilerOut string `json:"compiler_out"`
92+
CompilerErr string `json:"compiler_err"`
93+
BuilderResult cmdBuilderRes `json:"builder_result"`
17694
}
177-
178-
// Filter lines beginning with ERROR or WARNING
179-
cppCode = []byte(filterErrorsAndWarnings(cppCode))
180-
181-
err = ioutil.WriteFile(cppPath, cppCode, 0600)
182-
if err != nil {
183-
err = errors.Wrap(err, "Error while writing target file to temporary directory.")
184-
} else if enableLogging {
185-
log.Println("Target file written to", cppPath)
95+
var res cmdRes
96+
if err := json.Unmarshal(cmdOutput.Bytes(), &res); err != nil {
97+
return nil, errors.Errorf("parsing arduino-cli output: %s", err)
18698
}
187-
return
99+
100+
// Return only the build path
101+
log.Println("arduino-cli output:", cmdOutput)
102+
return res.BuilderResult.BuildPath, nil
188103
}
189104

190105
func filterErrorsAndWarnings(cppCode []byte) string {

0 commit comments

Comments
 (0)