-- Mathlib/CategoryTheory/Sites/DenseSubsite/SheafEquiv.lean
lemma isIso_ranCounit_app_of_isDenseSubsite (Y : Sheaf J A) (U X) :
    IsIso ((yoneda.map ((G.op.ranCounit.app Y.val).app (op U))).app (op X))

Proof state:
case left.mk.mk.unit.op.intro.intro
C : Type u_1
D : Type u_2
inst✝⁴ : Category.{u_3, u_1} C
inst✝³ : Category.{u_4, u_2} D
G : C ⥤ D
J : GrothendieckTopology C
K : GrothendieckTopology D
A : Type w
inst✝² : Category.{w', w} A
inst✝¹ : ∀ (X : Dᵒᵖ), Limits.HasLimitsOfShape (StructuredArrow X G.op) A
inst✝ : IsDenseSubsite J K G
Y : Sheaf J A
U : C
X : A
f₁ f₂ : (yoneda.obj (((G.op.ran ⋙ (whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj G.op).obj Y.val).obj (op U))).obj (op X)
e :
  (yoneda.map ((G.op.ranCounit.app Y.val).app (op U))).app (op X) f₁ =
    (yoneda.map ((G.op.ranCounit.app Y.val).app (op U))).app (op X) f₂
W : C
g : G.obj W ⟶ G.obj U
V : C
iVW : V ⟶ W
iVU : V ⟶ U
e' : G.map iVU = G.map iVW ≫ g
this :
  f₁ ≫ (G.op.ranCounit.app Y.val).app (op U) ≫ Y.val.map iVU.op =
    f₂ ≫ (G.op.ranCounit.app Y.val).app (op U) ≫ Y.val.map iVU.op
⊢ f₁ ≫ (G.op.ran.obj Y.val).map (G.map iVU).op ≫ (G.op.ranCounit.app Y.val).app (op V) =
    f₂ ≫ (G.op.ran.obj Y.val).map (G.map iVU).op ≫ (G.op.ranCounit.app Y.val).app (op V)

Tactic:
erw [← NatTrans.naturality] at this

Result:
case left.mk.mk.unit.op.intro.intro
C : Type u_1
D : Type u_2
inst✝⁴ : Category.{u_3, u_1} C
inst✝³ : Category.{u_4, u_2} D
G : C ⥤ D
J : GrothendieckTopology C
K : GrothendieckTopology D
A : Type w
inst✝² : Category.{w', w} A
inst✝¹ : ∀ (X : Dᵒᵖ), Limits.HasLimitsOfShape (StructuredArrow X G.op) A
inst✝ : IsDenseSubsite J K G
Y : Sheaf J A
U : C
X : A
f₁ f₂ : (yoneda.obj (((G.op.ran ⋙ (whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj G.op).obj Y.val).obj (op U))).obj (op X)
e :
  (yoneda.map ((G.op.ranCounit.app Y.val).app (op U))).app (op X) f₁ =
    (yoneda.map ((G.op.ranCounit.app Y.val).app (op U))).app (op X) f₂
W : C
g : G.obj W ⟶ G.obj U
V : C
iVW : V ⟶ W
iVU : V ⟶ U
e' : G.map iVU = G.map iVW ≫ g
this :
  f₁ ≫
      (((whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj G.op).obj (G.op.ran.obj Y.val)).map iVU.op ≫
        (G.op.ranCounit.app Y.val).app (op V) =
    f₂ ≫
      (((whiskeringLeft Cᵒᵖ Dᵒᵖ A).obj G.op).obj (G.op.ran.obj Y.val)).map iVU.op ≫
        (G.op.ranCounit.app Y.val).app (op V)
⊢ f₁ ≫ (G.op.ran.obj Y.val).map (G.map iVU).op ≫ (G.op.ranCounit.app Y.val).app (op V) =
    f₂ ≫ (G.op.ran.obj Y.val).map (G.map iVU).op ≫ (G.op.ranCounit.app Y.val).app (op V)

Proof state:
case refine_2.mk.mk.unit.op.mk.mk.unit.op.mk.up.up.refl.refl.intro.intro.h
C : Type u_1
D : Type u_2
inst✝⁴ : Category.{u_3, u_1} C
inst✝³ : Category.{u_4, u_2} D
G : C ⥤ D
J : GrothendieckTopology C
K : GrothendieckTopology D
A : Type w
inst✝² : Category.{w', w} A
inst✝¹ : ∀ (X : Dᵒᵖ), Limits.HasLimitsOfShape (StructuredArrow X G.op) A
inst✝ : IsDenseSubsite J K G
Y : Sheaf J A
U : C
X : A
f : (yoneda.obj (((𝟭 (Cᵒᵖ ⥤ A)).obj Y.val).obj (op U))).obj (op X)
l : (X Y Z : C) → (f : X ⟶ Y) → (g : G.obj Y ⟶ G.obj Z) → (G.imageSieve g).arrows f → (X ⟶ Z)
hl :
  ∀ (X Y Z : C) (f : X ⟶ Y) (g : G.obj Y ⟶ G.obj Z) (hf : (G.imageSieve g).arrows f),
    G.map (l X Y Z f g hf) = G.map f ≫ g
W₁ W₂ : C
g : G.obj W₁ ⟶ G.obj U
i : W₂ ⟶ W₁
hi : 𝟙 (op (G.obj U)) ≫ g.op ≫ (G.map i.op.unop).op = g.op ≫ (G.map i.op.unop).op
⊢ ∀ (I : GrothendieckTopology.Cover.Arrow ⟨G.imageSieve (G.map i ≫ g), ⋯⟩),
    ⋯.amalgamate ⟨G.imageSieve (G.map i ≫ g), ⋯⟩ (fun I => f ≫ Y.val.map (l I.Y W₂ U I.f (G.map i ≫ g) ⋯).op) ⋯ ≫
        Y.val.map I.f.op =
      (⋯.amalgamate ⟨G.imageSieve g, ⋯⟩ (fun I => f ≫ Y.val.map (l I.Y W₁ U I.f g ⋯).op) ⋯ ≫ Y.val.map i.op) ≫
        Y.val.map I.f.op

Tactic:
intro I

Result:
case refine_2.mk.mk.unit.op.mk.mk.unit.op.mk.up.up.refl.refl.intro.intro.h
C : Type u_1
D : Type u_2
inst✝⁴ : Category.{u_3, u_1} C
inst✝³ : Category.{u_4, u_2} D
G : C ⥤ D
J : GrothendieckTopology C
K : GrothendieckTopology D
A : Type w
inst✝² : Category.{w', w} A
inst✝¹ : ∀ (X : Dᵒᵖ), Limits.HasLimitsOfShape (StructuredArrow X G.op) A
inst✝ : IsDenseSubsite J K G
Y : Sheaf J A
U : C
X : A
f : (yoneda.obj (((𝟭 (Cᵒᵖ ⥤ A)).obj Y.val).obj (op U))).obj (op X)
l : (X Y Z : C) → (f : X ⟶ Y) → (g : G.obj Y ⟶ G.obj Z) → (G.imageSieve g).arrows f → (X ⟶ Z)
hl :
  ∀ (X Y Z : C) (f : X ⟶ Y) (g : G.obj Y ⟶ G.obj Z) (hf : (G.imageSieve g).arrows f),
    G.map (l X Y Z f g hf) = G.map f ≫ g
W₁ W₂ : C
g : G.obj W₁ ⟶ G.obj U
i : W₂ ⟶ W₁
hi : 𝟙 (op (G.obj U)) ≫ g.op ≫ (G.map i.op.unop).op = g.op ≫ (G.map i.op.unop).op
I : GrothendieckTopology.Cover.Arrow ⟨G.imageSieve (G.map i ≫ g), ⋯⟩
⊢ ⋯.amalgamate ⟨G.imageSieve (G.map i ≫ g), ⋯⟩ (fun I => f ≫ Y.val.map (l I.Y W₂ U I.f (G.map i ≫ g) ⋯).op) ⋯ ≫
      Y.val.map I.f.op =
    (⋯.amalgamate ⟨G.imageSieve g, ⋯⟩ (fun I => f ≫ Y.val.map (l I.Y W₁ U I.f g ⋯).op) ⋯ ≫ Y.val.map i.op) ≫
      Y.val.map I.f.op

Proof state:
case refine_2.mk.mk.unit.op.mk.mk.unit.op.mk.up.up.refl.refl.intro.intro
C : Type u_1
D : Type u_2
inst✝⁴ : Category.{u_3, u_1} C
inst✝³ : Category.{u_4, u_2} D
G : C ⥤ D
J : GrothendieckTopology C
K : GrothendieckTopology D
A : Type w
inst✝² : Category.{w', w} A
inst✝¹ : ∀ (X : Dᵒᵖ), Limits.HasLimitsOfShape (StructuredArrow X G.op) A
inst✝ : IsDenseSubsite J K G
Y : Sheaf J A
U : C
X : A
f : (yoneda.obj (((𝟭 (Cᵒᵖ ⥤ A)).obj Y.val).obj (op U))).obj (op X)
l : (X Y Z : C) → (f : X ⟶ Y) → (g : G.obj Y ⟶ G.obj Z) → (G.imageSieve g).arrows f → (X ⟶ Z)
hl :
  ∀ (X Y Z : C) (f : X ⟶ Y) (g : G.obj Y ⟶ G.obj Z) (hf : (G.imageSieve g).arrows f),
    G.map (l X Y Z f g hf) = G.map f ≫ g
W₁ W₂ : C
g : G.obj W₁ ⟶ G.obj U
i : W₂ ⟶ W₁
hi : 𝟙 (op (G.obj U)) ≫ g.op ≫ (G.map i.op.unop).op = g.op ≫ (G.map i.op.unop).op
⊢ ⋯.amalgamate ⟨G.imageSieve (G.map i ≫ g), ⋯⟩ (fun I => f ≫ Y.val.map (l I.Y W₂ U I.f (G.map i ≫ g) ⋯).op) ⋯ =
    ⋯.amalgamate ⟨G.imageSieve g, ⋯⟩ (fun I => f ≫ Y.val.map (l I.Y W₁ U I.f g ⋯).op) ⋯ ≫ Y.val.map i.op

Tactic:
apply Y.2.hom_ext ⟨_, IsDenseSubsite.imageSieve_mem J K G (G.map i ≫ g)⟩

Result:
case refine_2.mk.mk.unit.op.mk.mk.unit.op.mk.up.up.refl.refl.intro.intro.h
C : Type u_1
D : Type u_2
inst✝⁴ : Category.{u_3, u_1} C
inst✝³ : Category.{u_4, u_2} D
G : C ⥤ D
J : GrothendieckTopology C
K : GrothendieckTopology D
A : Type w
inst✝² : Category.{w', w} A
inst✝¹ : ∀ (X : Dᵒᵖ), Limits.HasLimitsOfShape (StructuredArrow X G.op) A
inst✝ : IsDenseSubsite J K G
Y : Sheaf J A
U : C
X : A
f : (yoneda.obj (((𝟭 (Cᵒᵖ ⥤ A)).obj Y.val).obj (op U))).obj (op X)
l : (X Y Z : C) → (f : X ⟶ Y) → (g : G.obj Y ⟶ G.obj Z) → (G.imageSieve g).arrows f → (X ⟶ Z)
hl :
  ∀ (X Y Z : C) (f : X ⟶ Y) (g : G.obj Y ⟶ G.obj Z) (hf : (G.imageSieve g).arrows f),
    G.map (l X Y Z f g hf) = G.map f ≫ g
W₁ W₂ : C
g : G.obj W₁ ⟶ G.obj U
i : W₂ ⟶ W₁
hi : 𝟙 (op (G.obj U)) ≫ g.op ≫ (G.map i.op.unop).op = g.op ≫ (G.map i.op.unop).op
⊢ ∀ (I : GrothendieckTopology.Cover.Arrow ⟨G.imageSieve (G.map i ≫ g), ⋯⟩),
    ⋯.amalgamate ⟨G.imageSieve (G.map i ≫ g), ⋯⟩ (fun I => f ≫ Y.val.map (l I.Y W₂ U I.f (G.map i ≫ g) ⋯).op) ⋯ ≫
        Y.val.map I.f.op =
      (⋯.amalgamate ⟨G.imageSieve g, ⋯⟩ (fun I => f ≫ Y.val.map (l I.Y W₁ U I.f g ⋯).op) ⋯ ≫ Y.val.map i.op) ≫
        Y.val.map I.f.op