Skip to content

Commit 804136d

Browse files
committed
Implemented sketch rebuild
1 parent ff6c799 commit 804136d

File tree

3 files changed

+132
-76
lines changed

3 files changed

+132
-76
lines changed

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ go 1.12
44

55
replace github.com/arduino/arduino-cli => ../arduino-cli
66

7+
replace github.com/arduino/go-paths-helper => ../go-paths-helper
8+
79
require (
810
github.com/arduino/arduino-cli v0.0.0-20201201130510-05ce1509a4f1
911
github.com/arduino/go-paths-helper v1.3.3

handler/builder.go

+62-45
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,90 @@
11
package handler
22

33
import (
4-
"bufio"
54
"bytes"
65
"encoding/json"
7-
"io/ioutil"
86
"log"
9-
"os"
10-
"os/exec"
11-
"path/filepath"
127
"strings"
8+
"time"
139

1410
"github.com/arduino/arduino-cli/arduino/libraries"
1511
"github.com/arduino/arduino-cli/executils"
1612
"github.com/arduino/go-paths-helper"
17-
"github.com/arduino/go-properties-orderedmap"
1813
"github.com/pkg/errors"
1914
)
2015

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-
// }
16+
func (handler *InoHandler) scheduleRebuildEnvironment() {
17+
handler.rebuildSketchDeadlineMutex.Lock()
18+
defer handler.rebuildSketchDeadlineMutex.Unlock()
19+
d := time.Now().Add(time.Second)
20+
handler.rebuildSketchDeadline = &d
21+
}
22+
23+
func (handler *InoHandler) rebuildEnvironmentLoop() {
24+
grabDeadline := func() *time.Time {
25+
handler.rebuildSketchDeadlineMutex.Lock()
26+
defer handler.rebuildSketchDeadlineMutex.Unlock()
27+
28+
res := handler.rebuildSketchDeadline
29+
handler.rebuildSketchDeadline = nil
30+
return res
31+
}
32+
33+
for {
34+
// Wait for someone to schedule a preprocessing...
35+
time.Sleep(100 * time.Millisecond)
36+
deadline := grabDeadline()
37+
if deadline == nil {
38+
continue
39+
}
3140

32-
func updateCpp(inoCode []byte, sourcePath, fqbn string, fqbnChanged bool, cppPath string) (cppCode []byte, err error) {
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-
// }
41+
for time.Now().Before(*deadline) {
42+
time.Sleep(100 * time.Millisecond)
4643

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-
// }
44+
if d := grabDeadline(); d != nil {
45+
deadline = d
46+
}
47+
}
5848

59-
// // Generate target file
60-
// cppCode, err = generateTargetFile(tempDir, inoPath, cppPath, fqbn)
61-
return
49+
// Regenerate preprocessed sketch!
50+
handler.synchronizer.DataMux.Lock()
51+
handler.initializeWorkbench(nil)
52+
handler.synchronizer.DataMux.Unlock()
53+
}
6254
}
6355

64-
func generateBuildEnvironment(sketchDir *paths.Path, fqbn string) (*paths.Path, error) {
56+
func (handler *InoHandler) generateBuildEnvironment() (*paths.Path, error) {
57+
sketchDir := handler.sketchRoot
58+
fqbn := handler.config.SelectedBoard.Fqbn
59+
60+
// Export temporary files
61+
type overridesFile struct {
62+
Overrides map[string]string `json:"overrides"`
63+
}
64+
data := overridesFile{Overrides: map[string]string{}}
65+
for uri, trackedFile := range handler.trackedFiles {
66+
rel, err := uri.AsPath().RelFrom(handler.sketchRoot)
67+
if err != nil {
68+
return nil, errors.WithMessage(err, "dumping tracked files")
69+
}
70+
data.Overrides[rel.String()] = trackedFile.Text
71+
}
72+
var overridesJSON string
73+
if jsonBytes, err := json.MarshalIndent(data, "", " "); err != nil {
74+
return nil, errors.WithMessage(err, "dumping tracked files")
75+
} else if tmpFile, err := paths.WriteToTempFile(jsonBytes, nil, ""); err != nil {
76+
return nil, errors.WithMessage(err, "dumping tracked files")
77+
} else {
78+
overridesJSON = tmpFile.String()
79+
}
80+
6581
// XXX: do this from IDE or via gRPC
6682
args := []string{globalCliPath,
6783
"compile",
6884
"--fqbn", fqbn,
6985
"--only-compilation-database",
7086
"--clean",
87+
"--source-override", overridesJSON,
7188
"--format", "json",
7289
sketchDir.String(),
7390
}

handler/handler.go

+68-31
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"regexp"
1010
"strconv"
1111
"strings"
12+
"sync"
1213
"time"
1314

1415
"github.com/arduino/arduino-cli/arduino/builder"
@@ -59,26 +60,30 @@ func NewInoHandler(stdio io.ReadWriteCloser, board lsp.Board) *InoHandler {
5960
if enableLogging {
6061
log.Println("Initial board configuration:", board)
6162
}
63+
64+
go handler.rebuildEnvironmentLoop()
6265
return handler
6366
}
6467

6568
// InoHandler is a JSON-RPC handler that delegates messages to clangd.
6669
type InoHandler struct {
67-
StdioConn *jsonrpc2.Conn
68-
ClangdConn *jsonrpc2.Conn
69-
lspInitializeParams *lsp.InitializeParams
70-
buildPath *paths.Path
71-
buildSketchRoot *paths.Path
72-
buildSketchCpp *paths.Path
73-
buildSketchCppVersion int
74-
buildSketchSymbols []lsp.DocumentSymbol
75-
buildSketchSymbolsLoad bool
76-
buildSketchSymbolsCheck bool
77-
sketchRoot *paths.Path
78-
sketchName string
79-
sketchMapper *sourcemapper.InoMapper
80-
sketchTrackedFilesCount int
81-
trackedFiles map[lsp.DocumentURI]*lsp.TextDocumentItem
70+
StdioConn *jsonrpc2.Conn
71+
ClangdConn *jsonrpc2.Conn
72+
lspInitializeParams *lsp.InitializeParams
73+
buildPath *paths.Path
74+
buildSketchRoot *paths.Path
75+
buildSketchCpp *paths.Path
76+
buildSketchCppVersion int
77+
buildSketchSymbols []lsp.DocumentSymbol
78+
buildSketchSymbolsLoad bool
79+
buildSketchSymbolsCheck bool
80+
rebuildSketchDeadline *time.Time
81+
rebuildSketchDeadlineMutex sync.Mutex
82+
sketchRoot *paths.Path
83+
sketchName string
84+
sketchMapper *sourcemapper.InoMapper
85+
sketchTrackedFilesCount int
86+
trackedFiles map[lsp.DocumentURI]*lsp.TextDocumentItem
8287

8388
config lsp.BoardConfig
8489
synchronizer Synchronizer
@@ -324,13 +329,18 @@ func (handler *InoHandler) exit() {
324329
}
325330

326331
func (handler *InoHandler) initializeWorkbench(params *lsp.InitializeParams) error {
327-
rootURI := params.RootURI
328-
log.Printf("--> initializeWorkbench(%s)\n", rootURI)
332+
currCppTextVersion := 0
333+
if params != nil {
334+
log.Printf("--> initialize(%s)\n", params.RootURI)
335+
handler.lspInitializeParams = params
336+
handler.sketchRoot = params.RootURI.AsPath()
337+
handler.sketchName = handler.sketchRoot.Base()
338+
} else {
339+
currCppTextVersion = handler.sketchMapper.CppText.Version
340+
log.Printf("--> RE-initialize()\n")
341+
}
329342

330-
handler.lspInitializeParams = params
331-
handler.sketchRoot = rootURI.AsPath()
332-
handler.sketchName = handler.sketchRoot.Base()
333-
if buildPath, err := generateBuildEnvironment(handler.sketchRoot, handler.config.SelectedBoard.Fqbn); err == nil {
343+
if buildPath, err := handler.generateBuildEnvironment(); err == nil {
334344
handler.buildPath = buildPath
335345
handler.buildSketchRoot = buildPath.Join("sketch")
336346
} else {
@@ -343,22 +353,48 @@ func (handler *InoHandler) initializeWorkbench(params *lsp.InitializeParams) err
343353

344354
if cppContent, err := handler.buildSketchCpp.ReadFile(); err == nil {
345355
handler.sketchMapper = sourcemapper.CreateInoMapper(cppContent)
356+
handler.sketchMapper.CppText.Version = currCppTextVersion + 1
346357
} else {
347358
return errors.WithMessage(err, "reading generated cpp file from sketch")
348359
}
349360

350-
clangdStdout, clangdStdin, clangdStderr := startClangd(handler.buildPath, handler.buildSketchCpp)
351-
clangdStdio := streams.NewReadWriteCloser(clangdStdin, clangdStdout)
352-
if enableLogging {
353-
clangdStdio = streams.LogReadWriteCloserAs(clangdStdio, "inols-clangd.log")
354-
go io.Copy(streams.OpenLogFileAs("inols-clangd-err.log"), clangdStderr)
361+
if params == nil {
362+
// If we are restarting re-synchronize clangd
363+
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
364+
defer cancel()
365+
366+
cppURI := lsp.NewDocumenteURIFromPath(handler.buildSketchCpp)
367+
cppTextDocumentIdentifier := lsp.TextDocumentIdentifier{URI: cppURI}
368+
369+
syncEvent := &lsp.DidChangeTextDocumentParams{
370+
TextDocument: lsp.VersionedTextDocumentIdentifier{
371+
TextDocumentIdentifier: cppTextDocumentIdentifier,
372+
Version: handler.sketchMapper.CppText.Version,
373+
},
374+
ContentChanges: []lsp.TextDocumentContentChangeEvent{
375+
{Text: handler.sketchMapper.CppText.Text}, // Full text change
376+
},
377+
}
378+
379+
if err := handler.ClangdConn.Notify(ctx, "textDocument/didChange", syncEvent); err != nil {
380+
log.Println(" error reinitilizing clangd:", err)
381+
return err
382+
}
355383
} else {
356-
go io.Copy(os.Stderr, clangdStderr)
357-
}
384+
// Otherwise start clangd!
385+
clangdStdout, clangdStdin, clangdStderr := startClangd(handler.buildPath, handler.buildSketchCpp)
386+
clangdStdio := streams.NewReadWriteCloser(clangdStdin, clangdStdout)
387+
if enableLogging {
388+
clangdStdio = streams.LogReadWriteCloserAs(clangdStdio, "inols-clangd.log")
389+
go io.Copy(streams.OpenLogFileAs("inols-clangd-err.log"), clangdStderr)
390+
} else {
391+
go io.Copy(os.Stderr, clangdStderr)
392+
}
358393

359-
clangdStream := jsonrpc2.NewBufferedStream(clangdStdio, jsonrpc2.VSCodeObjectCodec{})
360-
clangdHandler := jsonrpc2.AsyncHandler(jsonrpc2.HandlerWithError(handler.FromClangd))
361-
handler.ClangdConn = jsonrpc2.NewConn(context.Background(), clangdStream, clangdHandler)
394+
clangdStream := jsonrpc2.NewBufferedStream(clangdStdio, jsonrpc2.VSCodeObjectCodec{})
395+
clangdHandler := jsonrpc2.AsyncHandler(jsonrpc2.HandlerWithError(handler.FromClangd))
396+
handler.ClangdConn = jsonrpc2.NewConn(context.Background(), clangdStream, clangdHandler)
397+
}
362398

363399
return nil
364400
}
@@ -483,6 +519,7 @@ func (handler *InoHandler) didChange(ctx context.Context, req *lsp.DidChangeText
483519
// and trigger arduino-preprocessing + clangd restart.
484520

485521
log.Println(" uh oh DIRTY CHANGE!")
522+
handler.scheduleRebuildEnvironment()
486523
}
487524

488525
// log.Println("New version:----------")

0 commit comments

Comments
 (0)