@@ -24,14 +24,18 @@ import {
24
24
ArchiveSketchRequest ,
25
25
LoadSketchRequest ,
26
26
} from './cli-protocol/cc/arduino/cli/commands/v1/commands_pb' ;
27
+ import { duration } from '../common/decorators' ;
28
+ import * as glob from 'glob' ;
27
29
28
30
const WIN32_DRIVE_REGEXP = / ^ [ a - z A - Z ] : \\ / ;
29
31
30
32
const prefix = '.arduinoIDE-unsaved' ;
31
33
32
34
@injectable ( )
33
- export class SketchesServiceImpl extends CoreClientAware
34
- implements SketchesService {
35
+ export class SketchesServiceImpl
36
+ extends CoreClientAware
37
+ implements SketchesService
38
+ {
35
39
private sketchSuffixIndex = 1 ;
36
40
private lastSketchBaseName : string ;
37
41
@@ -43,13 +47,147 @@ export class SketchesServiceImpl extends CoreClientAware
43
47
44
48
@inject ( EnvVariablesServer )
45
49
protected readonly envVariableServer : EnvVariablesServer ;
50
+
46
51
async getSketches ( {
47
52
uri,
48
53
exclude,
49
54
} : {
50
55
uri ?: string ;
51
56
exclude ?: string [ ] ;
52
- } ) : Promise < SketchContainerWithDetails > {
57
+ } ) : Promise < SketchContainer > {
58
+ const [ old , _new ] = await Promise . all ( [
59
+ this . getSketchesOld ( { uri, exclude } ) ,
60
+ this . getSketchesNew ( { uri, exclude } ) ,
61
+ ] ) ;
62
+ console . log ( typeof old ) ;
63
+ return _new ;
64
+ }
65
+
66
+ @duration ( )
67
+ async getSketchesNew ( {
68
+ uri,
69
+ exclude,
70
+ } : {
71
+ uri ?: string ;
72
<
D7AE
code class="diff-text syntax-highlighted-line addition">+ exclude ?: string [ ] ;
73
+ } ) : Promise < SketchContainer > {
74
+ const root = await this . root ( uri ) ;
75
+ const pathToAllSketchFiles = await new Promise < string [ ] > (
76
+ ( resolve , reject ) => {
77
+ glob (
78
+ '/!(libraries|hardware)/**/*.{ino,pde}' ,
79
+ { root } ,
80
+ ( error , results ) => {
81
+ if ( error ) {
82
+ reject ( error ) ;
83
+ } else {
84
+ resolve ( results ) ;
85
+ }
86
+ }
87
+ ) ;
88
+ }
89
+ ) ;
90
+ // Sort by path length to filter out nested sketches, such as the `Nested_folder` inside the `Folder` sketch.
91
+ //
92
+ // `directories#user`
93
+ // |
94
+ // +--Folder
95
+ // |
96
+ // +--Folder.ino
97
+ // |
98
+ // +--Nested_folder
99
+ // |
100
+ // +--Nested_folder.ino
101
+ pathToAllSketchFiles . sort ( ( left , right ) => left . length - right . length ) ;
102
+ const container = SketchContainer . create (
103
+ uri ? path . basename ( root ) : 'Sketchbook'
104
+ ) ;
105
+ const getOrCreateChildContainer = (
106
+ parent : SketchContainer ,
107
+ segments : string [ ]
108
+ ) => {
109
+ if ( segments . length === 1 ) {
110
+ throw new Error (
111
+ `Expected at least two segments relative path: ['ExampleSketchName', 'ExampleSketchName.{ino,pde}]. Was: ${ segments } `
112
+ ) ;
113
+ }
114
+ if ( segments . length === 2 ) {
115
+ return parent ;
116
+ }
117
+ const label = segments [ 0 ] ;
118
+ const existingSketch = parent . sketches . find (
119
+ ( sketch ) => sketch . name === label
120
+ ) ;
121
+ if ( existingSketch ) {
122
+ // If the container has a sketch with the same label, it cannot have a child container.
123
+ // See above example about how to ignore nested sketches.
124
+ return undefined ;
125
+ }
126
+ let child = parent . children . find ( ( child ) => child . label === label ) ;
127
+ if ( ! child ) {
128
+ child = SketchContainer . create ( label ) ;
129
+ parent . children . push ( child ) ;
130
+ }
131
+ return child ;
132
+ } ;
133
+ for ( const pathToSketchFile of pathToAllSketchFiles ) {
134
+ const relative = path . relative ( root , pathToSketchFile ) ;
135
+ if ( ! relative ) {
136
+ console . warn (
137
+ `Could not determine relative sketch path from the root <${ root } > to the sketch <${ pathToSketchFile } >. Skipping. Relative path was: ${ relative } `
138
+ ) ;
139
+ continue ;
140
+ }
141
+ const segments = relative . split ( path . sep ) ;
142
+ if ( segments . length < 2 ) {
143
+ // folder name, and sketch name.
144
+ console . warn (
145
+ `Expected at least one segment relative path from the root <${ root } > to the sketch <${ pathToSketchFile } >. Skipping. Segments were: ${ segments } .`
146
+ ) ;
147
+ continue ;
148
+ }
149
+ // the folder name and the sketch name must match. For example, `Foo/foo.ino` is invalid.
150
+ // drop the folder name from the sketch name, if `.ino` or `.pde` remains, it's valid
151
+ const sketchName = segments [ segments . length - 2 ] ;
152
+ const sketchFilename = segments [ segments . length - 1 ] ;
153
+ const sketchFileExtension = segments [ segments . length - 1 ] . replace (
154
+ new RegExp ( sketchName ) ,
155
+ ''
156
+ ) ;
157
+ if
10000
( sketchFileExtension !== '.ino' && sketchFileExtension !== '.pde' ) {
158
+ console . warn (
159
+ `Mismatching sketch file <${ sketchFilename } >and sketch folder name <${ sketchName } >. Skipping`
160
+ ) ;
161
+ continue ;
162
+ }
163
+ const child = getOrCreateChildContainer ( container , segments ) ;
164
+ if ( child ) {
165
+ child . sketches . push ( {
166
+ name : sketchName ,
167
+ uri : FileUri . create ( pathToSketchFile ) . toString ( ) ,
168
+ } ) ;
169
+ }
170
+ }
171
+ return container ;
172
+ }
173
+
174
+ private async root ( uri ?: string | undefined ) : Promise < string > {
175
+ return FileUri . fsPath ( uri ?? ( await this . sketchbookUri ( ) ) ) ;
176
+ }
177
+
178
+ private async sketchbookUri ( ) : Promise < string > {
179
+ const { sketchDirUri } = await this . configService . getConfiguration ( ) ;
180
+ return sketchDirUri ;
181
+ }
182
+
183
+ @duration ( )
184
+ async getSketchesOld ( {
185
+ uri,
186
+ exclude,
187
+ } : {
188
+ uri ?: string ;
189
+ exclude ?: string [ ] ;
190
+ } ) : Promise < SketchContainer > {
53
191
const start = Date . now ( ) ;
54
192
let sketchbookPath : undefined | string ;
55
193
if ( ! uri ) {
0 commit comments