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)
0 commit comments