Skip to content

Commit cbf4246

Browse files
Copilottninja
andcommitted
Finish syncing ECA integration with latest upstream bridge and ext updates
Co-authored-by: tninja <714625+tninja@users.noreply.github.com>
1 parent b8460fb commit cbf4246

4 files changed

Lines changed: 92 additions & 42 deletions

File tree

ai-code-eca.el

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ Currently informational only."
114114
(project-root project))))
115115
default-directory))
116116

117+
(defun ai-code-eca--normalize-folder-path (path)
118+
"Return PATH as an expanded directory path without a trailing slash."
119+
(directory-file-name (expand-file-name path)))
120+
117121
(defun ai-code-eca--save-session-affinity ()
118122
"Remember ECA as the preferred ai-code backend for the current project."
119123
(when-let* ((root (ai-code-eca--project-root))
@@ -439,11 +443,11 @@ With prefix ARG, force a new session."
439443
(project-roots
440444
(delete-dups
441445
(mapcar (lambda (root)
442-
(directory-file-name (expand-file-name root)))
446+
(ai-code-eca--normalize-folder-path root))
443447
project-roots-raw)))
444448
(existing
445449
(mapcar (lambda (root)
446-
(directory-file-name (expand-file-name root)))
450+
(ai-code-eca--normalize-folder-path root))
447451
(or (when (fboundp 'eca-list-workspace-folders)
448452
(eca-list-workspace-folders session))
449453
(when (fboundp 'eca--session-workspace-folders)

eca-ext.el

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@
6262
(declare-function project-current "project" (&optional maybe-prompt dir))
6363
(declare-function project-root "project" (project))
6464

65+
(defun eca-ext--normalize-folder-path (path)
66+
"Return PATH as an expanded directory path without a trailing slash."
67+
(directory-file-name (expand-file-name path)))
68+
6569
;;; Session Multiplexing
6670

6771
(defun eca-list-sessions ()
@@ -448,9 +452,12 @@ If the value is `prompt', ask before creating."
448452

449453
(defvar eca--shared-context nil
450454
"Plist of shared context items available to all sessions.
451-
Keys currently used are :files and :repo-maps.")
455+
Keys currently used are :files and :repo-maps.
456+
457+
- :files contains a list of absolute file paths.
458+
- :repo-maps contains a list of absolute repository root directories.")
452459

453-
(defun eca--file-project-root (file-path)
460+
(defun eca-ext--file-project-root (file-path)
454461
"Return a project root for FILE-PATH using projectile, project.el, or fallback."
455462
(when file-path
456463
(or (when (fboundp 'projectile-project-root)
@@ -463,38 +470,36 @@ Keys currently used are :files and :repo-maps.")
463470
(project-root proj)))))
464471
(file-name-directory file-path))))
465472

466-
(defun eca--session-for-project-root (project-root)
473+
(defun eca-ext--session-for-project-root (project-root)
467474
"Find the ECA session whose workspace contains PROJECT-ROOT."
468-
(let* ((root (directory-file-name (expand-file-name project-root)))
475+
(let* ((root (eca-ext--normalize-folder-path project-root))
469476
(sessions (eca-list-sessions)))
470477
(cl-dolist (info sessions)
471478
(let* ((session-id (plist-get info :id))
472479
(folders (plist-get info :workspace-folders))
473480
(match (cl-find root folders
474481
:test (lambda (lhs rhs)
475-
(string= lhs
476-
(directory-file-name
477-
(expand-file-name rhs)))))))
482+
(string= lhs (eca-ext--normalize-folder-path rhs))))))
478483
(when match
479484
(cl-return session-id))))))
480485

481-
(defun eca--auto-add-workspace-hook ()
482-
"Auto-add the current file's project root to the current ECA workspace."
486+
(defun eca-ext--auto-add-workspace-hook ()
487+
"Auto-add the current file's project root to the current ECA workspace.
488+
If the project is already present in the workspace, do nothing."
483489
(when (and eca-auto-add-workspace-folder
484490
buffer-file-name
485491
(featurep 'eca)
486492
(eca-session))
487-
(let* ((project-root (eca--file-project-root buffer-file-name))
493+
(let* ((project-root (eca-ext--file-project-root buffer-file-name))
488494
(session (eca-session))
489495
(workspace-folders (eca--session-workspace-folders session))
490496
(in-workspace (and project-root
491-
(member (directory-file-name (expand-file-name project-root))
497+
(member (eca-ext--normalize-folder-path project-root)
492498
(mapcar (lambda (folder)
493-
(directory-file-name
494-
(expand-file-name folder)))
499+
(eca-ext--normalize-folder-path folder))
495500
workspace-folders)))))
496501
(when (and project-root (not in-workspace))
497-
(let ((root (directory-file-name (expand-file-name project-root))))
502+
(let ((root (eca-ext--normalize-folder-path project-root)))
498503
(pcase eca-auto-add-workspace-folder
499504
('t
500505
(eca--session-add-workspace-folder session root)
@@ -504,20 +509,20 @@ Keys currently used are :files and :repo-maps.")
504509
(when (y-or-n-p (format "Add project to ECA workspace? (%s) " root))
505510
(eca--session-add-workspace-folder session root)))))))))
506511

507-
(defun eca--auto-switch-session-hook (&optional _frame)
512+
(defun eca-ext--auto-switch-session-hook (&optional _frame)
508513
"Auto-switch ECA sessions when the active project changes."
509514
(when (and eca-auto-switch-session
510515
buffer-file-name
511516
(featurep 'eca)
512517
(eca-list-sessions))
513-
(let* ((project-root (eca--file-project-root buffer-file-name))
518+
(let* ((project-root (eca-ext--file-project-root buffer-file-name))
514519
(current-session (ignore-errors (eca-session)))
515520
(current-session-id (when current-session
516521
(ignore-errors (eca--session-id current-session)))))
517522
(when (and project-root
518523
(not (string= project-root eca--last-project-root)))
519-
(let ((target-session (eca--session-for-project-root project-root))
520-
(root (directory-file-name (expand-file-name project-root))))
524+
(let ((target-session (eca-ext--session-for-project-root project-root))
525+
(root (eca-ext--normalize-folder-path project-root)))
521526
(setq eca--last-project-root root)
522527
(when (and target-session
523528
(not (eq target-session current-session-id)))
@@ -531,17 +536,17 @@ Keys currently used are :files and :repo-maps.")
531536
target-session root))
532537
(eca-switch-to-session target-session))))))))))
533538

534-
(defun eca--auto-create-session-hook ()
539+
(defun eca-ext--auto-create-session-hook ()
535540
"Auto-create or extend ECA sessions when visiting a project without one."
536541
(when (and eca-auto-create-session
537542
buffer-file-name
538543
(featurep 'eca))
539-
(let* ((project-root (eca--file-project-root buffer-file-name))
544+
(let* ((project-root (eca-ext--file-project-root buffer-file-name))
540545
(existing-session (when project-root
541-
(eca--session-for-project-root project-root)))
546+
(eca-ext--session-for-project-root project-root)))
542547
(any-sessions (eca-list-sessions)))
543548
(when (and project-root (not existing-session))
544-
(let ((root (directory-file-name (expand-file-name project-root))))
549+
(let ((root (eca-ext--normalize-folder-path project-root)))
545550
(pcase eca-auto-create-session
546551
('t
547552
(if any-sessions
@@ -565,50 +570,52 @@ Keys currently used are :files and :repo-maps.")
565570
(when session
566571
(eca-chat-open session)))))))))))
567572

568-
(defun eca--auto-sync-workspace-hook (&optional _frame)
573+
(defun eca-ext--auto-sync-workspace-hook (&optional _frame)
569574
"Auto-sync the current project's root into the current ECA workspace."
570575
(when (and eca-auto-sync-workspace
571576
buffer-file-name
572577
(featurep 'eca)
573578
(eca-session))
574-
(let* ((project-root (eca--file-project-root buffer-file-name)))
579+
(let* ((project-root (eca-ext--file-project-root buffer-file-name)))
575580
(when project-root
576-
(let* ((root (directory-file-name (expand-file-name project-root)))
581+
(let* ((root (eca-ext--normalize-folder-path project-root))
577582
(session (eca-session))
578583
(folders (eca--session-workspace-folders session))
579584
(in-workspace (member root
580-
(mapcar (lambda (folder)
581-
(directory-file-name
582-
(expand-file-name folder)))
585+
(mapcar #'eca-ext--normalize-folder-path
583586
folders))))
584587
(unless in-workspace
585588
(eca--session-add-workspace-folder session root)
586589
(message "Auto-synced workspace: added %s" root)))))))
587590

588591
(with-eval-after-load 'eca
589-
(add-hook 'find-file-hook #'eca--auto-add-workspace-hook)
590-
(add-hook 'find-file-hook #'eca--auto-create-session-hook 90)
591-
(add-hook 'window-buffer-change-functions #'eca--auto-switch-session-hook)
592-
(add-hook 'window-buffer-change-functions #'eca--auto-sync-workspace-hook))
592+
(add-hook 'find-file-hook #'eca-ext--auto-add-workspace-hook)
593+
(add-hook 'find-file-hook #'eca-ext--auto-create-session-hook 90)
594+
(add-hook 'window-buffer-change-functions #'eca-ext--auto-switch-session-hook)
595+
(add-hook 'window-buffer-change-functions #'eca-ext--auto-sync-workspace-hook))
593596

594597
;;; Shared Context
595598

596599
(defun eca-share-file-context (file-path)
597600
"Add FILE-PATH to the shared context for all ECA sessions."
598601
(interactive "fShare file across sessions: ")
599602
(let ((file-path (expand-file-name file-path)))
600-
(unless (plist-get eca--shared-context :files)
601-
(setq eca--shared-context (plist-put eca--shared-context :files nil)))
602-
(cl-pushnew file-path (plist-get eca--shared-context :files) :test #'string=)
603+
(setq eca--shared-context
604+
(plist-put
605+
eca--shared-context
606+
:files
607+
(cl-adjoin file-path (plist-get eca--shared-context :files) :test #'string=)))
603608
(message "Shared file across all ECA sessions: %s" file-path)))
604609

605610
(defun eca-share-repo-map-context (project-root)
606611
"Add PROJECT-ROOT repo map to the shared context for all ECA sessions."
607612
(interactive "DShare repo map across sessions: ")
608613
(let ((root (expand-file-name project-root)))
609-
(unless (plist-get eca--shared-context :repo-maps)
610-
(setq eca--shared-context (plist-put eca--shared-context :repo-maps nil)))
611-
(cl-pushnew root (plist-get eca--shared-context :repo-maps) :test #'string=)
614+
(setq eca--shared-context
615+
(plist-put
616+
eca--shared-context
617+
:repo-maps
618+
(cl-adjoin root (plist-get eca--shared-context :repo-maps) :test #'string=)))
612619
(message "Shared repo map across all ECA sessions: %s" root)))
613620

614621
(defun eca-apply-shared-context (session)
@@ -623,6 +630,11 @@ Keys currently used are :files and :repo-maps.")
623630
(eca-chat-add-file-context session file)))
624631
(dolist (root repo-maps)
625632
(when (file-directory-p root)
633+
(unless (member (eca-ext--normalize-folder-path root)
634+
(mapcar #'eca-ext--normalize-folder-path
635+
(or (eca-list-workspace-folders session) '())))
636+
(when (fboundp 'eca-add-workspace-folder)
637+
(eca-add-workspace-folder root session)))
626638
(eca-chat-add-repo-map-context session)))
627639
(message "Applied shared context to session %d: %d files, %d repo maps"
628640
(eca--session-id session)

test/test_ai-code-eca.el

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
"mock-input")
2424
(provide 'ai-code-input))
2525

26+
(defvar package-vc-selected-packages)
27+
2628
(require 'ai-code-eca)
2729

2830
(ert-deftest ai-code-test-eca-start-forwards-prefix-arg ()
@@ -234,6 +236,34 @@
234236
(ai-code-eca-share-file "/tmp/example.txt")
235237
(should (equal shared-file "/tmp/example.txt")))))
236238

239+
(ert-deftest ai-code-test-eca-upgrade-vc-uses-package-vc-when-selected ()
240+
"Ensure VC-installed ECA upgrades through `package-vc-upgrade'."
241+
(let ((package-vc-selected-packages '((eca . "https://example.test/eca.git")))
242+
called)
243+
(provide 'package-vc)
244+
(cl-letf (((symbol-function 'package-vc-upgrade)
245+
(lambda (pkg) (setq called pkg)))
246+
((symbol-function 'message) (lambda (&rest _args) nil)))
247+
(ai-code-eca-upgrade-vc)
248+
(should (eq called 'eca)))))
249+
250+
(ert-deftest ai-code-test-eca-sync-context-delegates-to-context-helpers ()
251+
"Ensure `ai-code-eca-sync-context' sends file and cursor context."
252+
(let (file-calls cursor-calls)
253+
(cl-letf (((symbol-function 'eca-session) (lambda () 'mock-session))
254+
((symbol-function 'eca-chat-add-file-context)
255+
(lambda (_session file-path) (push file-path file-calls)))
256+
((symbol-function 'eca-chat-add-cursor-context)
257+
(lambda (_session file-path pos)
258+
(push (list file-path pos) cursor-calls)))
259+
((symbol-function 'message) (lambda (&rest _args) nil)))
260+
(with-temp-buffer
261+
(setq buffer-file-name "/tmp/example.el")
262+
(goto-char (point-min))
263+
(ai-code-eca-sync-context))
264+
(should (equal file-calls '("/tmp/example.el")))
265+
(should (equal cursor-calls '(("/tmp/example.el" 1)))))))
266+
237267
(provide 'test_ai-code-eca)
238268

239269
;;; test_ai-code-eca.el ends here

test/test_eca-ext.el

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@
4848
"Ensure shared context application delegates to per-session helpers."
4949
(let ((eca--shared-context '(:files ("/tmp/a.txt")
5050
:repo-maps ("/tmp/project")))
51-
file-calls repo-map-calls)
51+
file-calls repo-map-calls workspace-adds)
5252
(cl-letf (((symbol-function 'file-exists-p) (lambda (path) (string= path "/tmp/a.txt")))
5353
((symbol-function 'file-directory-p) (lambda (path) (string= path "/tmp/project")))
54+
((symbol-function 'eca-list-workspace-folders) (lambda (_session) nil))
55+
((symbol-function 'eca-add-workspace-folder)
56+
(lambda (root _session) (push root workspace-adds)))
5457
((symbol-function 'eca-chat-add-file-context)
5558
(lambda (_session file-path) (push file-path file-calls)))
5659
((symbol-function 'eca-chat-add-repo-map-context)
@@ -59,7 +62,8 @@
5962
((symbol-function 'message) (lambda (&rest _args) nil)))
6063
(eca-apply-shared-context 'mock-session)
6164
(should (equal file-calls '("/tmp/a.txt")))
62-
(should (equal repo-map-calls '(t))))))
65+
(should (equal repo-map-calls '(t)))
66+
(should (equal workspace-adds '("/tmp/project"))))))
6367

6468
(provide 'test_eca-ext)
6569

0 commit comments

Comments
 (0)