@@ -265,68 +265,103 @@ enum FetchStatus {
265265}
266266
267267fn fetch_grammar ( grammar : GrammarConfiguration ) -> Result < FetchStatus > {
268- if let GrammarSource :: Git {
268+ let GrammarSource :: Git {
269269 remote, revision, ..
270270 } = grammar. source
271- {
272- let grammar_dir = crate :: runtime_dirs ( )
271+ else {
272+ return Ok ( FetchStatus :: NonGit ) ;
273+ } ;
274+
275+ let repo = VendoredGrammar :: new ( & grammar. grammar_id ) ;
276+
277+ // WARN: Must init before other operations are done.
278+ repo. init ( & remote) ?;
279+
280+ if repo. revision ( ) . as_ref ( ) == Some ( & revision) {
281+ return Ok ( FetchStatus :: GitUpToDate ) ;
282+ }
283+
284+ // Fetch the grammar if the revision doesn't match.
285+ repo. fetch ( & remote, & revision) ?;
286+
287+ Ok ( FetchStatus :: GitUpdated { revision } )
288+ }
289+
290+ struct VendoredGrammar {
291+ dir : PathBuf ,
292+ }
293+
294+ impl VendoredGrammar {
295+ fn new ( grammar : & str ) -> Self {
296+ let dir = crate :: runtime_dirs ( )
273297 . first ( )
274298 . expect ( "No runtime directories provided" ) // guaranteed by post-condition
275299 . join ( "grammars" )
276300 . join ( "sources" )
277- . join ( & grammar. grammar_id ) ;
301+ . join ( grammar) ;
302+
303+ Self { dir }
304+ }
305+
306+ /// Gets the current revision of the repo.
307+ fn revision ( & self ) -> Option < String > {
308+ git ( & self . dir , [ "rev-parse" , "HEAD" ] ) . ok ( )
309+ }
278310
279- fs:: create_dir_all ( & grammar_dir) . context ( format ! (
311+ /// Fetches grammar at the given revision.
312+ ///
313+ /// To ensure clean state, existing grammar directory is removed and re-inited
314+ /// before fetch operation.
315+ fn fetch ( & self , remote : & str , rev : & str ) -> Result < ( ) > {
316+ self . reinit ( remote) ?;
317+
318+ git ( & self . dir , [ "fetch" , "--depth" , "1" , REMOTE_NAME , rev] ) ?;
319+ git ( & self . dir , [ "checkout" , rev] ) ?;
320+
321+ Ok ( ( ) )
322+ }
323+
324+ /// Initializes the grammar directory.
325+ ///
326+ /// Creates directory and sets it up as a git repo, with remote set correctly.
327+ fn init ( & self , remote : & str ) -> Result < ( ) > {
328+ // Create the grammar directory if needed.
329+ fs:: create_dir_all ( & self . dir ) . context ( format ! (
280330 "Could not create grammar directory {:?}" ,
281- grammar_dir
331+ & self . dir
282332 ) ) ?;
283333
284- // create the grammar dir contains a git directory
285- if !grammar_dir . join ( ".git" ) . exists ( ) {
286- git ( & grammar_dir , [ "init" ] ) ?;
334+ // Ensure directory is git initialized.
335+ if !self . dir . join ( ".git" ) . exists ( ) {
336+ git ( & self . dir , [ "init" ] ) ?;
287337 }
288338
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) ?;
339+ // Ensure the remote matches the configured remote, setting if needed.
340+ if self . remote ( ) . as_deref ( ) != Some ( remote) {
341+ self . set_remote ( remote) ?;
292342 }
293343
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 )
344+ Ok ( ( ) )
311345 }
312- }
313346
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- }
347+ /// Removes the grammar directory before initializing again.
348+ fn reinit ( & self , remote : & str ) -> Result < ( ) > {
349+ fs:: remove_dir_all ( & self . dir ) ?;
350+ self . init ( remote) ?;
351+ Ok ( ( ) )
352+ }
323353
324- fn get_remote_url ( repository_dir : & Path ) -> Option < String > {
325- git ( repository_dir, [ "remote" , "get-url" , REMOTE_NAME ] ) . ok ( )
326- }
354+ /// Gets remote URL of grammar repo.
355+ fn remote ( & self ) -> Option < String > {
356+ git ( & self . dir , [ "remote" , "get-url" , REMOTE_NAME ] ) . ok ( )
357+ }
327358
328- fn get_revision ( repository_dir : & Path ) -> Option < String > {
329- git ( repository_dir, [ "rev-parse" , "HEAD" ] ) . ok ( )
359+ /// Sets remote URL of grammar repo.
360+ fn set_remote ( & self , remote : & str ) -> Result < ( ) > {
361+ git ( & self . dir , [ "remote" , "set-url" , REMOTE_NAME , remote] )
362+ . or_else ( |_| git ( & self . dir , [ "remote" , "add" , REMOTE_NAME , remote] ) ) ?;
363+ Ok ( ( ) )
364+ }
330365}
331366
332367// A wrapper around 'git' commands which returns stdout in success and a
0 commit comments