1use crate::editor::git_memory::{ContextKey, GitJob};
3use crate::editor::Editor;
4use crate::files::FileTarget;
5use strop_core::worker::{CancelReason, Load, Outcome};
6use strop_git::Repo;
7
8impl Editor {
9 pub fn discover_git(&mut self) {
17 if self.docs.is_empty() || self.finishing {
18 return;
19 }
20 if let Some(context) = self.cur().git_context().cloned() {
21 if let Load::Running(ticket) = &self.git_discovery {
22 self.cancel_git_worker(ticket.request, CancelReason::Superseded);
23 }
24 self.git_discovery = Load::Idle;
25 if self.git.as_ref() != Some(&context) {
26 self.git = Some(context);
27 self.invalidate_git_view();
28 }
29 return;
30 }
31 self.git_discovery.retry_failed();
32 if let Some(file) = self.remote_file().cloned() {
33 self.discover_remote_git(file);
34 return;
35 }
36 if let crate::editor::document::DocumentSource::Container { container, path } =
39 &self.cur().source
40 {
41 self.discover_container_git(container.clone(), path.clone());
42 return;
43 }
44 let from = self
45 .buf()
46 .path
47 .as_deref()
48 .map(std::path::PathBuf::from)
49 .unwrap_or_else(|| self.cwd.clone());
50 if matches!(&self.git_discovery, Load::Running(current) if current.key.from == FileTarget::Local(from.clone()))
53 {
54 return;
55 }
56 let running = match &self.git_discovery {
57 Load::Running(current) => Some(current.request),
58 _ => None,
59 };
60 if let Some(request) = running {
61 self.cancel_git_worker(request, CancelReason::Superseded);
62 }
63 let Some(ticket) = self.git_ticket(ContextKey {
64 from: FileTarget::Local(from.clone()),
65 }) else {
66 return;
67 };
68 self.git_discovery = Load::Running(ticket.clone());
69 strop_trace::record_with(strop_trace::EventKind::JobStarted, || {
70 serde_json::json!({
71 "service":"git","request":"discover","target":"local","from":from.to_string_lossy(),
72 })
73 });
74 let args = ticket.clone();
75 self.launch_git_job(
76 "git-discover",
77 "git.discover",
78 ticket,
79 &args,
80 GitJob::Context,
81 move |cancel| {
82 if cancel.is_cancelled() {
83 return Outcome::Cancelled(CancelReason::Superseded);
84 }
85 Outcome::Success(Repo::discover(&from).map(|repo| repo.context()))
86 },
87 );
88 }
89
90 fn discover_remote_git(&mut self, file: strop_workspace::RemoteFile) {
94 let endpoint = file.endpoint().clone();
95 let from_dir = if self.remote_directory().is_some() {
96 file.path().to_owned()
97 } else {
98 file.path()
99 .parent()
100 .unwrap_or(std::path::Path::new("/"))
101 .to_owned()
102 };
103 let from = FileTarget::Remote(file.into());
104 if matches!(&self.git_discovery, Load::Running(current) if current.key.from == from) {
105 return;
106 }
107 let running = match &self.git_discovery {
108 Load::Running(current) => Some(current.request),
109 _ => None,
110 };
111 if let Some(request) = running {
112 self.cancel_git_worker(request, CancelReason::Superseded);
113 }
114 let Some(ticket) = self.git_ticket(ContextKey { from }) else {
115 return;
116 };
117 self.git_discovery = Load::Running(ticket.clone());
118 strop_trace::record_with(strop_trace::EventKind::JobStarted, || {
119 serde_json::json!({
120 "service":"git","request":"discover","target":"remote",
121 "endpoint":endpoint.to_string(),"from":from_dir.to_string_lossy(),
122 })
123 });
124 let args = ticket.clone();
125 self.launch_git_job(
126 "git-discover-remote",
127 "git.discover",
128 ticket,
129 &args,
130 GitJob::Context,
131 move |cancel| {
132 if cancel.is_cancelled() {
133 return Outcome::Cancelled(CancelReason::Superseded);
134 }
135 match strop_git::remote::discover(&endpoint, &from_dir, &cancel) {
136 Ok(None) => Outcome::Success(None),
137 Ok(Some(workdir)) => {
138 match strop_git::remote::context(&endpoint, &workdir, &cancel) {
139 Ok(context) => Outcome::Success(Some(context)),
140 Err(error) => Outcome::Failed {
141 failure: strop_core::worker::Failure::new(
142 strop_core::worker::FailureKind::Exit,
143 error.to_string(),
144 ),
145 partial: None,
146 },
147 }
148 }
149 Err(error) => Outcome::Failed {
150 failure: strop_core::worker::Failure::new(
151 strop_core::worker::FailureKind::Exit,
152 error.to_string(),
153 ),
154 partial: None,
155 },
156 }
157 },
158 );
159 }
160 fn discover_container_git(
164 &mut self,
165 container: strop_workspace::ContainerId,
166 path: std::path::PathBuf,
167 ) {
168 let from_dir = path
169 .parent()
170 .unwrap_or(std::path::Path::new("/"))
171 .to_owned();
172 let from = FileTarget::Container {
173 container: container.clone(),
174 path: from_dir.clone(),
175 };
176 if matches!(&self.git_discovery, Load::Running(current) if current.key.from == from) {
177 return;
178 }
179 let running = match &self.git_discovery {
180 Load::Running(current) => Some(current.request),
181 _ => None,
182 };
183 if let Some(request) = running {
184 self.cancel_git_worker(request, CancelReason::Superseded);
185 }
186 let Some(ticket) = self.git_ticket(ContextKey { from }) else {
187 return;
188 };
189 self.git_discovery = Load::Running(ticket.clone());
190 strop_trace::record_with(strop_trace::EventKind::JobStarted, || {
191 serde_json::json!({
192 "service":"git","request":"discover","target":"container",
193 "container":container.to_string(),"from":from_dir.to_string_lossy(),
194 })
195 });
196 let args = ticket.clone();
197 self.launch_git_job(
198 "git-discover-container",
199 "git.discover",
200 ticket,
201 &args,
202 GitJob::Context,
203 move |cancel| {
204 if cancel.is_cancelled() {
205 return Outcome::Cancelled(CancelReason::Superseded);
206 }
207 let fail = |error: &dyn ToString| Outcome::Failed {
208 failure: strop_core::worker::Failure::new(
209 strop_core::worker::FailureKind::Exit,
210 error.to_string(),
211 ),
212 partial: None,
213 };
214 match strop_git::container::discover(&container, &from_dir, &cancel) {
215 Ok(None) => Outcome::Success(None),
216 Ok(Some(workdir)) => {
217 match strop_git::container::context(&container, &workdir, &cancel) {
218 Ok(context) => Outcome::Success(Some(context)),
219 Err(error) => fail(&error),
220 }
221 }
222 Err(error) => fail(&error),
223 }
224 },
225 );
226 }
227
228 pub(crate) fn git_context(&self) -> Option<&strop_git::GitContext> {
229 self.panes
230 .get(self.active_pane)
231 .and_then(|pane| self.docs.get(pane.doc))
232 .and_then(crate::editor::Document::git_context)
233 .or(self.git.as_ref())
234 }
235}