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 let directory = self.directory().map(|source| source.location.clone());
33 if let Some(directory) = &directory {
34 match &directory.filesystem {
35 strop_workspace::Filesystem::Remote(endpoint) => {
36 match strop_workspace::RemoteFile::from_path(
37 endpoint.clone(),
38 directory.path.clone(),
39 ) {
40 Ok(file) => self.discover_remote_git(file),
41 Err(error) => self.message = error.to_string(),
42 }
43 return;
44 }
45 strop_workspace::Filesystem::Container(container) => {
46 self.discover_container_git(container.clone(), directory.path.clone());
47 return;
48 }
49 strop_workspace::Filesystem::Local => {}
50 }
51 }
52 if let Some(file) = self.remote_file().cloned() {
53 self.discover_remote_git(file);
54 return;
55 }
56 if let crate::editor::document::DocumentSource::Container { container, path } =
59 &self.cur().source
60 {
61 self.discover_container_git(container.clone(), path.clone());
62 return;
63 }
64 let from = directory
65 .map(|directory| directory.path)
66 .or_else(|| self.buf().path.clone())
67 .unwrap_or_else(|| self.cwd.clone());
68 if matches!(&self.git_discovery, Load::Running(current) if current.key.from == FileTarget::Local(from.clone()))
71 {
72 return;
73 }
74 let running = match &self.git_discovery {
75 Load::Running(current) => Some(current.request),
76 _ => None,
77 };
78 if let Some(request) = running {
79 self.cancel_git_worker(request, CancelReason::Superseded);
80 }
81 let Some(ticket) = self.git_ticket(ContextKey {
82 from: FileTarget::Local(from.clone()),
83 }) else {
84 return;
85 };
86 self.git_discovery = Load::Running(ticket.clone());
87 strop_trace::record_with(strop_trace::EventKind::JobStarted, || {
88 serde_json::json!({
89 "service":"git","request":"discover","target":"local","from":from.to_string_lossy(),
90 })
91 });
92 let args = ticket.clone();
93 self.launch_git_job(
94 "git-discover",
95 "git.discover",
96 ticket,
97 &args,
98 GitJob::Context,
99 move |cancel| {
100 if cancel.is_cancelled() {
101 return Outcome::Cancelled(CancelReason::Superseded);
102 }
103 Outcome::Success(Repo::discover(&from).map(|repo| repo.context()))
104 },
105 );
106 }
107
108 fn discover_remote_git(&mut self, file: strop_workspace::RemoteFile) {
112 let endpoint = file.endpoint().clone();
113 let from_dir = if self.directory().is_some() {
114 file.path().to_owned()
115 } else {
116 file.path()
117 .parent()
118 .unwrap_or(std::path::Path::new("/"))
119 .to_owned()
120 };
121 let from = FileTarget::Remote(file.into());
122 if matches!(&self.git_discovery, Load::Running(current) if current.key.from == from) {
123 return;
124 }
125 let running = match &self.git_discovery {
126 Load::Running(current) => Some(current.request),
127 _ => None,
128 };
129 if let Some(request) = running {
130 self.cancel_git_worker(request, CancelReason::Superseded);
131 }
132 let Some(ticket) = self.git_ticket(ContextKey { from }) else {
133 return;
134 };
135 self.git_discovery = Load::Running(ticket.clone());
136 strop_trace::record_with(strop_trace::EventKind::JobStarted, || {
137 serde_json::json!({
138 "service":"git","request":"discover","target":"remote",
139 "endpoint":endpoint.to_string(),"from":from_dir.to_string_lossy(),
140 })
141 });
142 let args = ticket.clone();
143 self.launch_git_job(
144 "git-discover-remote",
145 "git.discover",
146 ticket,
147 &args,
148 GitJob::Context,
149 move |cancel| {
150 if cancel.is_cancelled() {
151 return Outcome::Cancelled(CancelReason::Superseded);
152 }
153 match strop_git::remote::discover(&endpoint, &from_dir, &cancel) {
154 Ok(None) => Outcome::Success(None),
155 Ok(Some(workdir)) => {
156 match strop_git::remote::context(&endpoint, &workdir, &cancel) {
157 Ok(context) => Outcome::Success(Some(context)),
158 Err(error) => Outcome::Failed {
159 failure: strop_core::worker::Failure::new(
160 strop_core::worker::FailureKind::Exit,
161 error.to_string(),
162 ),
163 partial: None,
164 },
165 }
166 }
167 Err(error) => Outcome::Failed {
168 failure: strop_core::worker::Failure::new(
169 strop_core::worker::FailureKind::Exit,
170 error.to_string(),
171 ),
172 partial: None,
173 },
174 }
175 },
176 );
177 }
178 fn discover_container_git(
182 &mut self,
183 container: strop_workspace::ContainerId,
184 path: std::path::PathBuf,
185 ) {
186 let from_dir = if self.directory().is_some() {
187 path.clone()
188 } else {
189 path.parent()
190 .unwrap_or(std::path::Path::new("/"))
191 .to_owned()
192 };
193 let from = FileTarget::Container {
194 container: container.clone(),
195 path: from_dir.clone(),
196 };
197 if matches!(&self.git_discovery, Load::Running(current) if current.key.from == from) {
198 return;
199 }
200 let running = match &self.git_discovery {
201 Load::Running(current) => Some(current.request),
202 _ => None,
203 };
204 if let Some(request) = running {
205 self.cancel_git_worker(request, CancelReason::Superseded);
206 }
207 let Some(ticket) = self.git_ticket(ContextKey { from }) else {
208 return;
209 };
210 self.git_discovery = Load::Running(ticket.clone());
211 strop_trace::record_with(strop_trace::EventKind::JobStarted, || {
212 serde_json::json!({
213 "service":"git","request":"discover","target":"container",
214 "container":container.to_string(),"from":from_dir.to_string_lossy(),
215 })
216 });
217 let args = ticket.clone();
218 self.launch_git_job(
219 "git-discover-container",
220 "git.discover",
221 ticket,
222 &args,
223 GitJob::Context,
224 move |cancel| {
225 if cancel.is_cancelled() {
226 return Outcome::Cancelled(CancelReason::Superseded);
227 }
228 let fail = |error: &dyn ToString| Outcome::Failed {
229 failure: strop_core::worker::Failure::new(
230 strop_core::worker::FailureKind::Exit,
231 error.to_string(),
232 ),
233 partial: None,
234 };
235 match strop_git::container::discover(&container, &from_dir, &cancel) {
236 Ok(None) => Outcome::Success(None),
237 Ok(Some(workdir)) => {
238 match strop_git::container::context(&container, &workdir, &cancel) {
239 Ok(context) => Outcome::Success(Some(context)),
240 Err(error) => fail(&error),
241 }
242 }
243 Err(error) => fail(&error),
244 }
245 },
246 );
247 }
248
249 pub(crate) fn git_context(&self) -> Option<&strop_git::GitContext> {
250 self.panes
251 .get(self.active_pane)
252 .and_then(|pane| self.docs.get(pane.doc))
253 .and_then(crate::editor::Document::git_context)
254 .or(self.git.as_ref())
255 }
256}