Skip to content

Commit 8c20277

Browse files
committed
Support relative contexts additions via , and .
Fixes #61
1 parent e916a5d commit 8c20277

File tree

3 files changed

+115
-14
lines changed

3 files changed

+115
-14
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- Support relative contexts additions via `~`, `./` `../` and `/`. #61
6+
57
## 0.35.0
68

79
- Anthropic subscription support, via `/login anthropic` command. #57

src/eca/features/context.clj

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262

6363
(defn ^:private contexts-for [root-filename query config]
6464
(let [all-paths (fs/glob root-filename "**")
65-
query (some-> query string/trim)
6665
filtered (if (or (nil? query) (string/blank? query))
6766
all-paths
6867
(filter (fn [p]
@@ -73,23 +72,48 @@
7372
allowed-files))
7473

7574
(defn all-contexts [query db* config]
76-
(let [all-subfiles-and-dirs (into []
77-
(comp
78-
(map :uri)
79-
(map shared/uri->filename)
80-
(mapcat #(contexts-for % query config))
81-
(take 200) ;; for performance, user can always make query specific for better results.
82-
(map (fn [file-or-dir]
83-
{:type (if (fs/directory? file-or-dir)
84-
"directory"
85-
"file")
86-
:path (str (fs/canonicalize file-or-dir))})))
87-
(:workspace-folders @db*))
75+
(let [query (or (some-> query string/trim) "")
76+
first-project-path (shared/uri->filename (:uri (first (:workspace-folders @db*))))
77+
relative-path (and query
78+
(or
79+
(when (string/starts-with? query "~")
80+
(fs/expand-home (fs/file query)))
81+
(when (string/starts-with? query "/")
82+
(fs/file query))
83+
(when (or (string/starts-with? query "./")
84+
(string/starts-with? query "../"))
85+
(fs/file first-project-path query))))
86+
relative-files (when relative-path
87+
(mapv
88+
(fn [file-or-dir]
89+
{:type (if (fs/directory? file-or-dir)
90+
"directory"
91+
"file")
92+
:path (str (fs/canonicalize file-or-dir))})
93+
(try
94+
(if (fs/exists? relative-path)
95+
(fs/list-dir relative-path)
96+
(fs/list-dir (fs/parent relative-path)))
97+
(catch Exception _ nil))))
98+
workspace-files (when-not relative-path
99+
(into []
100+
(comp
101+
(map :uri)
102+
(map shared/uri->filename)
103+
(mapcat #(contexts-for % query config))
104+
(take 200) ;; for performance, user can always make query specific for better results.
105+
(map (fn [file-or-dir]
106+
{:type (if (fs/directory? file-or-dir)
107+
"directory"
108+
"file")
109+
:path (str (fs/canonicalize file-or-dir))})))
110+
(:workspace-folders @db*)))
88111
root-dirs (mapv (fn [{:keys [uri]}] {:type "directory"
89112
:path (shared/uri->filename uri)})
90113
(:workspace-folders @db*))
91114
mcp-resources (mapv #(assoc % :type "mcpResource") (f.mcp/all-resources @db*))]
92115
(concat [{:type "repoMap"}]
93116
root-dirs
94-
all-subfiles-and-dirs
117+
relative-files
118+
workspace-files
95119
mcp-resources)))

test/eca/features/context_test.clj

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,78 @@
100100
results (f.context/all-contexts "readme" db* config)
101101
file-paths (->> results (filter #(= "file" (:type %))) (map :path) set)]
102102
(is (contains? file-paths readme)))))))
103+
104+
(deftest relative-path-query-test
105+
(testing "./relative path lists entries in that directory (no glob)"
106+
(let [root (h/file-path "/fake/repo")
107+
rel (str root "/./src")
108+
entries [(str rel "/a.clj") (str rel "/pkg")]
109+
db* (atom {:workspace-folders [{:uri (h/file-uri "file:///fake/repo")}]})]
110+
(with-redefs [fs/glob (fn [& _] (throw (ex-info "glob should not be called for relative paths" {})))
111+
fs/file (fn [& parts] (string/join "/" parts))
112+
fs/exists? (fn [p] (= p rel))
113+
fs/list-dir (fn [p]
114+
(is (= p rel))
115+
entries)
116+
fs/parent (fn [_] (throw (ex-info "parent should not be used when path exists" {})))
117+
fs/directory? (fn [p] (string/ends-with? (str p) "/pkg"))
118+
fs/canonicalize identity
119+
f.index/filter-allowed (fn [file-paths _root _config] file-paths)
120+
f.mcp/all-resources (fn [_] [])]
121+
(let [result (f.context/all-contexts "./src" db* {})]
122+
;; Root directory present
123+
(is (some #(= {:type "directory" :path root} %) result))
124+
;; Entries mapped from the relative listing
125+
(is (some #(= {:type "file" :path (str rel "/a.clj")} %) result))
126+
(is (some #(= {:type "directory" :path (str rel "/pkg")} %) result))))))
127+
128+
(testing "./relative path falls back to parent directory when non-existent"
129+
(let [root (h/file-path "/fake/repo")
130+
rel (str root "/./missing/file")
131+
parent (str root "/./missing")
132+
entries [(str parent "/x.txt") (str parent "/subdir")]
133+
db* (atom {:workspace-folders [{:uri (h/file-uri "file:///fake/repo")}]})]
134+
(with-redefs [fs/glob (fn [& _] (throw (ex-info "glob should not be called for relative paths" {})))
135+
fs/file (fn [& parts] (string/join "/" parts))
136+
fs/exists? (fn [p] (= p "exists-nowhere")) ;; ensure rel does not exist
137+
fs/list-dir (fn [p]
138+
(is (= p parent))
139+
entries)
140+
fs/parent (fn [p]
141+
(is (= p rel))
142+
parent)
143+
fs/directory? (fn [p] (string/ends-with? (str p) "/subdir"))
144+
fs/canonicalize identity
145+
f.index/filter-allowed (fn [file-paths _root _config] file-paths)
146+
f.mcp/all-resources (fn [_] [])]
147+
(let [result (f.context/all-contexts "./missing/file" db* {})]
148+
;; Root directory present
149+
(is (some #(= {:type "directory" :path root} %) result))
150+
;; Entries mapped from the parent listing
151+
(is (some #(= {:type "file" :path (str parent "/x.txt")} %) result))
152+
(is (some #(= {:type "directory" :path (str parent "/subdir")} %) result))))))
153+
154+
(testing "~ expands to home and lists entries"
155+
(let [root (h/file-path "/fake/repo")
156+
home "/home/tester"
157+
entries [(str home "/.bashrc") (str home "/projects")]
158+
db* (atom {:workspace-folders [{:uri (h/file-uri "file:///fake/repo")}]})]
159+
(with-redefs [fs/glob (fn [& _] (throw (ex-info "glob should not be called for ~ paths" {})))
160+
fs/file (fn [& parts] (string/join "/" parts))
161+
fs/expand-home (fn [p]
162+
(is (= p "~"))
163+
home)
164+
fs/exists? (fn [p] (= p home))
165+
fs/list-dir (fn [p]
166+
(is (= p home))
167+
entries)
168+
fs/directory? (fn [p] (string/ends-with? (str p) "/projects"))
169+
fs/canonicalize identity
170+
f.index/filter-allowed (fn [file-paths _root _config] file-paths)
171+
f.mcp/all-resources (fn [_] [])]
172+
(let [result (f.context/all-contexts "~" db* {})]
173+
;; Root directory present
174+
(is (some #(= {:type "directory" :path root} %) result))
175+
;; Entries from home listing
176+
(is (some #(= {:type "file" :path (str home "/.bashrc")} %) result))
177+
(is (some #(= {:type "directory" :path (str home "/projects")} %) result)))))))

0 commit comments

Comments
 (0)