@@ -264,69 +264,104 @@ enum FetchStatus {
264264 NonGit ,
265265}
266266
267- fn fetch_grammar ( grammar : GrammarConfiguration ) -> Result < FetchStatus > {
268- if let GrammarSource :: Git {
269- remote, revision, ..
270- } = grammar. source
271- {
272- let grammar_dir = crate :: runtime_dirs ( )
267+ struct VendoredGrammar {
268+ dir : PathBuf ,
269+ }
270+
271+ impl VendoredGrammar {
272+ fn new ( grammar : & str ) -> Self {
273+ let dir = crate :: runtime_dirs ( )
273274 . first ( )
274275 . expect ( "No runtime directories provided" ) // guaranteed by post-condition
275276 . join ( "grammars" )
276277 . join ( "sources" )
277- . join ( & grammar. grammar_id ) ;
278+ . join ( grammar) ;
279+
280+ Self { dir }
281+ }
282+
283+ /// Gets the current revision of the repo.
284+ fn revision ( & self ) -> Option < String > {
285+ git ( & self . dir , [ "rev-parse" , "HEAD" ] ) . ok ( )
286+ }
278287
279- fs:: create_dir_all ( & grammar_dir) . context ( format ! (
288+ /// Fetches grammar at the given revision.
289+ ///
290+ /// To ensure clean state, existing grammar directory is removed and re-inited
291+ /// before fetch operation.
292+ fn fetch ( & self , remote : & str , rev : & str ) -> Result < ( ) > {
293+ self . reinit ( remote) ?;
294+
295+ git ( & self . dir , [ "fetch" , "--depth" , "1" , REMOTE_NAME , rev] ) ?;
296+ git ( & self . dir , [ "checkout" , rev] ) ?;
297+
298+ Ok ( ( ) )
299+ }
300+
301+ /// Initializes the grammar directory.
302+ ///
303+ /// Creates directory and sets it up as a git repo, with remote set correctly.
304+ fn init ( & self , remote : & str ) -> Result < ( ) > {
305+ // Create the grammar directory if needed.
306+ fs:: create_dir_all ( & self . dir ) . context ( format ! (
280307 "Could not create grammar directory {:?}" ,
281- grammar_dir
308+ & self . dir
282309 ) ) ?;
283310
284- // create the grammar dir contains a git directory
285- if !grammar_dir . join ( ".git" ) . exists ( ) {
286- git ( & grammar_dir , [ "init" ] ) ?;
311+ // Ensure directory is git initialized.
312+ if !self . dir . join ( ".git" ) . exists ( ) {
313+ git ( & self . dir , [ "init" ] ) ?;
287314 }
288315
289- // ensure the remote matches the configured remote
290- if get_remote_url ( & grammar_dir ) . as_ref ( ) != Some ( & remote) {
291- set_remote ( & grammar_dir , & remote) ?;
316+ // Ensure the remote matches the configured remote, setting if needed.
317+ if self . remote ( ) . as_deref ( ) != Some ( remote) {
318+ self . set_remote ( remote) ?;
292319 }
293320
294- // ensure the revision matches the configured revision
295- if get_revision ( & grammar_dir) . as_ref ( ) != Some ( & revision) {
296- // Fetch the exact revision from the remote.
297- // Supported by server-side git since v2.5.0 (July 2015),
298- // enabled by default on major git hosts.
299- git (
300- & grammar_dir,
301- [ "fetch" , "--depth" , "1" , REMOTE_NAME , & revision] ,
302- ) ?;
303- git ( & grammar_dir, [ "checkout" , & revision] ) ?;
304-
305- Ok ( FetchStatus :: GitUpdated { revision } )
306- } else {
307- Ok ( FetchStatus :: GitUpToDate )
308- }
309- } else {
310- Ok ( FetchStatus :: NonGit )
321+ Ok ( ( ) )
311322 }
312- }
313323
314- // Sets the remote for a repository to the given URL, creating the remote if
315- // it does not yet exist.
316- fn set_remote ( repository_dir : & Path , remote_url : & str ) -> Result < String > {
317- git (
318- repository_dir,
319- [ "remote" , "set-url" , REMOTE_NAME , remote_url] ,
320- )
321- . or_else ( |_| git ( repository_dir, [ "remote" , "add" , REMOTE_NAME , remote_url] ) )
322- }
324+ /// Removes the grammar directory before initializing again.
325+ fn reinit ( & self , remote : & str ) -> Result < ( ) > {
326+ fs:: remove_dir_all ( & self . dir ) ?;
327+ self . init ( remote) ?;
328+ Ok ( ( ) )
329+ }
330+
331+ /// Gets remote URL of grammar repo.
332+ fn remote ( & self ) -> Option < String > {
333+ git ( & self . dir , [ "remote" , "get-url" , REMOTE_NAME ] ) . ok ( )
334+ }
323335
324- fn get_remote_url ( repository_dir : & Path ) -> Option < String > {
325- git ( repository_dir, [ "remote" , "get-url" , REMOTE_NAME ] ) . ok ( )
336+ /// Sets remote URL of grammar repo.
337+ fn set_remote ( & self , remote : & str ) -> Result < ( ) > {
338+ git ( & self . dir , [ "remote" , "set-url" , REMOTE_NAME , remote] )
339+ . or_else ( |_| git ( & self . dir , [ "remote" , "add" , REMOTE_NAME , remote] ) ) ?;
340+ Ok ( ( ) )
341+ }
326342}
327343
328- fn get_revision ( repository_dir : & Path ) -> Option < String > {
329- git ( repository_dir, [ "rev-parse" , "HEAD" ] ) . ok ( )
344+ fn fetch_grammar ( grammar : GrammarConfiguration ) -> Result < FetchStatus > {
345+ let GrammarSource :: Git {
346+ remote, revision, ..
347+ } = grammar. source
348+ else {
349+ return Ok ( FetchStatus :: NonGit ) ;
350+ } ;
351+
352+ let repo = VendoredGrammar :: new ( & grammar. grammar_id ) ;
353+
354+ // WARN: Must init before other operations are done.
355+ repo. init ( & remote) ?;
356+
357+ if repo. revision ( ) . is_some_and ( |rev| rev == revision) {
358+ return Ok ( FetchStatus :: GitUpToDate ) ;
359+ }
360+
361+ // Fetch the grammar if the revision doesn't match.
362+ repo. fetch ( & remote, & revision) ?;
363+
364+ Ok ( FetchStatus :: GitUpdated { revision } )
330365}
331366
332367// A wrapper around 'git' commands which returns stdout in success and a
0 commit comments