@@ -265,49 +265,92 @@ 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) ;
278302
279- fs:: create_dir_all ( & grammar_dir) . context ( format ! (
303+ Self { dir }
304+ }
305+
306+ /// Gets the current revision of the repo.
307+ fn revision ( & self ) -> Option < String > {
308+ get_revision ( & self . dir )
309+ }
310+
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.
340+ if get_remote_url ( & self . dir ) . as_deref ( ) != Some ( remote) {
341+ set_remote ( & self . dir , 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 ( ( ) )
345+ }
346+
347+ /// Removes the grammar directory before initializing again.
348+ fn reinit ( & self , remote : & str ) -> Result < ( ) > {
349+ fs:: remove_dir_all ( & self . dir ) ?;
350+
351+ self . init ( remote) ?;
352+
353+ Ok ( ( ) )
311354 }
312355}
313356
0 commit comments