• Stars
    star
    114
  • Rank 308,031 (Top 7 %)
  • Language
    Clojure
  • License
    Eclipse Public Li...
  • Created almost 6 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Clojure[Script] source code indexer

clindex

Clindex is a general and extensible Clojure[Script] source code indexer. It scans a Clojure[Script] project together with all its dependencies and generates a datascript database with facts about them.

It is intended to be used as a platform for building dev tools so they don't have to deal with the complexities of understanding Clojure code by reading the filesystem. Instead, as an api for talking about your code it gives you a datascript db full of facts you can use together with d/q, d/pull, d/entity, etc.

Features

  • Index your project and all its dependency tree (only lein and deps.edn supported so far)
  • Big set of facts out of the box, see schema
  • Extensible, you can make any form generate any facts by adding a method for the clindex.forms-facts.core/form-facts multimethod
  • Hot reload, watches your sources and reindexes whenever something on its source path changes, taking care of retraction and notification

Installation

Clindex is available as a Maven artifact from Clojars.

The latest released version is: Clojars Project

Usage

(require '[clindex.api :as clindex])
(require '[datascript.core :as d])
(require '[clojure.string :as str])
(require '[clojure.pprint :as pprint])

;; first you index a project folder for some platforms
(clindex/index-project! "./" {:platforms #{:clj}})

;; then retrieve the datascript db for the platform you want to explore
(def db (clindex/db :clj))

;; now you can explore your code using datalog, pull or whatever you can run against datascript
;; lets query all the vars that start with "eval"
(->> (d/q '[:find ?vname ?nname ?pname ?vline ?fname
            :in $ ?text
            :where
            [?fid :file/name ?fname]
            [?pid :project/name ?pname]
            [?nid :namespace/file ?fid]
            [?pid :project/namespaces ?nid]
            [?nid :namespace/name ?nname]
            [?nid :namespace/vars ?vid]
            [?vid :var/name ?vname]
            [?vid :var/line ?vline]
            [(str/starts-with? ?vname ?text)]]
          db
          "eval")
     (map #(zipmap [:name :ns :project :line :file] %))
     (pprint/print-table))

;; =>

;; |         :name |               :ns |                  :project | :line |                                                                                                                       :file |
;; |---------------+-------------------+---------------------------+-------+-----------------------------------------------------------------------------------------------------------------------------|
;; |      eval-opt |      clojure.main |       org.clojure/clojure |   482 |                      jar:file:/home/jmonetta/.m2/repository/org/clojure/clojure/1.10.1/clojure-1.10.1.jar!/clojure/main.clj |
;; |      eval-str | cljs.repl.graaljs | org.clojure/clojurescript |    49 | jar:file:/home/jmonetta/.m2/repository/org/clojure/clojurescript/1.10.516/clojurescript-1.10.516.jar!/cljs/repl/graaljs.clj |
;; |   eval-result |   cljs.repl.rhino | org.clojure/clojurescript |    64 |   jar:file:/home/jmonetta/.m2/repository/org/clojure/clojurescript/1.10.516/clojurescript-1.10.516.jar!/cljs/repl/rhino.clj |
;; |      eval-opt |          cljs.cli | org.clojure/clojurescript |   260 |          jar:file:/home/jmonetta/.m2/repository/org/clojure/clojurescript/1.10.516/clojurescript-1.10.516.jar!/cljs/cli.clj |
;; |     eval-cljs |         cljs.repl | org.clojure/clojurescript |   682 |        jar:file:/home/jmonetta/.m2/repository/org/clojure/clojurescript/1.10.516/clojurescript-1.10.516.jar!/cljs/repl.cljc |
;; | evaluate-form |         cljs.repl | org.clojure/clojurescript |   499 |        jar:file:/home/jmonetta/.m2/repository/org/clojure/clojurescript/1.10.516/clojurescript-1.10.516.jar!/cljs/repl.cljc |
;; |      evaluate |         cljs.repl | org.clojure/clojurescript |   131 |        jar:file:/home/jmonetta/.m2/repository/org/clojure/clojurescript/1.10.516/clojurescript-1.10.516.jar!/cljs/repl.cljc |
;; | eval-resource | cljs.repl.graaljs | org.clojure/clojurescript |    52 | jar:file:/home/jmonetta/.m2/repository/org/clojure/clojurescript/1.10.516/clojurescript-1.10.516.jar!/cljs/repl/graaljs.clj |
;; |          eval |      clojure.core |       org.clojure/clojure |  3210 |                      jar:file:/home/jmonetta/.m2/repository/org/clojure/clojure/1.10.1/clojure-1.10.1.jar!/clojure/core.clj |

index-project! options should be a map with the following keys :

  • :platforms a set containing :clj and/or :cljs
  • :extra-schema a schema that will be merged with dbs schemas
  • :on-new-facts a fn of one arg that will be called with new facts everytime a file inside base-dir project sources changes

DB schema

You can find the schema here.

Extending clindex

You can extend clindex to make any form generate any facts by adding implementations of the clindex.forms-facts.core/form-facts multimethod.

There are already some extensions not loaded by default, take a look at /src/clindex/forms_facts/. For indexing re-frame facts for example just (require [clindex.forms_facts.re-frame :as re-frame-facts]) and when calling index-project! add to the :extra-schema re-frame-facts/extra-schema.

The dispatch value for clindex.forms-facts.core/form-facts is the fully qualified form first symbol. The method will receive as parameters :

  • all-namespaces-map (spec :scanner/namespaces)
  • ctx a context map that at least will contain :namespace/name and things like :in-function if the form is inside a fn definition
  • form the form with the first symbol fully qualified when it is a function, it also contains all metadata added by tools.reader + some more stuff

It should return a map with the following keys :

  • :ctx, the new context
  • :facts, a collection of datascript tx-data like [:db/add eid attr val]

Example: indexing compojure routes

(require '[clindex.forms-facts.core :as forms-facts])

(clindex/index-project! "./test-resources/test-project/"
                        {:platforms #{:clj}
                         :extra-schema {:compojure.route/method {:db/cardinality :db.cardinality/one}
                                        :compojure.route/url    {:db/cardinality :db.cardinality/one}}})

(defmethod forms-facts/form-facts 'compojure.core/GET
  [all-ns-map {:keys [:namespace/name] :as ctx} [_ url :as form]]

  (let [route-id (utils/stable-id :route :get url)]
    {:facts [[:db/add route-id :compojure.route/method :get]
             [:db/add route-id :compojure.route/url url]]
     :ctx ctx}))

(def db (clindex/db :clj))

(d/q '[:find ?rmeth ?rurl
       :in $
       :where
       [?rid :compojure.route/method ?rmeth]
       [?rid :compojure.route/url ?rurl]]
     db)

;; =>
;; #{[:get "/"]
;;   [:get "/test"]
;;   [:get (add-wildcard path)]}

Datalog examples

who calls clojure.core/juxt ?

(let [juxt-vid (d/q '[:find ?vid .
                      :in $ ?nsn ?vn
                      :where
                      [?nsid :namespace/name ?nsn]
                      [?nsid :namespace/vars ?vid]
                      [?vid :var/name ?vn]]
                    db
                    'clojure.core
                    'juxt)]
  (-> (d/pull db [{:var/refs [{:var-ref/namespace [:namespace/name]} :var-ref/line]}] juxt-vid)
      :var/refs
      (clojure.pprint/print-table)))

;; =>

;; | :var-ref/line |                                 :var-ref/namespace |
;; |---------------+----------------------------------------------------|
;; |          4215 |                   #:namespace{:name cljs.analyzer} |
;; |          1834 |                    #:namespace{:name cljs.closure} |
;; |            58 |                       #:namespace{:name cljs.main} |
;; |            55 |                       #:namespace{:name cljs.main} |
;; |           336 |                       #:namespace{:name cljs.repl} |
;; |          3934 |                   #:namespace{:name cljs.analyzer} |
;; |            37 |                       #:namespace{:name cljs.main} |
;; |           344 |      #:namespace{:name clojure.tools.analyzer.jvm} |
;; |          4186 |                   #:namespace{:name cljs.analyzer} |
;; |            80 |                       #:namespace{:name hawk.core} |
;; |          3355 |                   #:namespace{:name cljs.analyzer} |
;; |            97 |                #:namespace{:name cljs.core.server} |
;; |           163 |                 #:namespace{:name clindex.indexer} |
;; |           113 | #:namespace{:name clojure.tools.reader.impl.utils} |
;; |          2172 |                   #:namespace{:name cljs.analyzer} |
;; |            79 |                       #:namespace{:name hawk.core} |
;; |          2576 |                    #:namespace{:name clojure.core} |
;; |          1145 |                       #:namespace{:name cljs.repl} |
;; |          1994 |                    #:namespace{:name cljs.closure} |
;; |            90 |                   #:namespace{:name datascript.db} |
;; |           621 |                        #:namespace{:name cljs.cli} |

Projects known to be using clindex

  • Clograms Explore clojure codebases by building diagrams

For developers

This is a high level overview of the api and the scanner.

This is a high level overview of the indexer.

More Repositories

1

flow-storm-debugger

A debugger for Clojure and ClojureScript with some unique features.
Clojure
498
star
2

clograms

Clojure[Script] source code diagrams
Clojure
179
star
3

inspectable

In the spec table
Clojure
71
star
4

old-flow-storm

Tracing companion library for the flow-storm-debugger
Clojure
62
star
5

magic-sheet

Create magic sheets to improve your Clojure[Script] repl experience.
Clojure
24
star
6

hansel

Instrument Clojure[Script] forms to trace it
Clojure
21
star
7

clj-tree-layout

A library for laying out tree nodes in 2D space for Clojure and ClojureScript.
Clojure
17
star
8

cider-storm

An emacs cider front-end for the FlowStorm debugger with support for Clojure and ClojureScript.
Emacs Lisp
16
star
9

pretty-spec

Pretty print clojure.spec forms
Clojure
15
star
10

reagent-flowgraph

A reagent component for laying out tree nodes in 2D space.
Clojure
11
star
11

smart-view

Visualize and explore your solidity smart contracts.
Clojure
10
star
12

shadow-flow-storm-basic

A basic template for using shadow-cljs with FlowStorm debugger
HTML
5
star
13

dotFiles

Personal dot files like .stumpwmrc, .emacs, etc.
Emacs Lisp
5
star
14

xray

Debugging tools for clojure
Clojure
5
star
15

web-extractor

Framework for parsing/extracting data from web pages into a more manageable format
Common Lisp
5
star
16

re-mount-module-browser

A re-frame applications browser
Clojure
3
star
17

nrepl-flowstorm-debug

A minimal repo to debug nrepl and cider middleware using FlowStorm
Clojure
3
star
18

elisp-utils

A collection of elisp utilities I use on my projects
Emacs Lisp
3
star
19

fs-data-window-demo

Demo FlowStorm data-windows features
Clojure
1
star
20

events-pipes

Clojure
1
star
21

flow-stom-dart-debugging

A small template with instructions on how to use flow-storm to debug the ClojureDart compiler
1
star
22

emuland

Microcontrollers emulators in the browser
Clojure
1
star
23

london-meetup

Slides for the FlowStorm presentation of Londom Clojurians meetup group
Clojure
1
star
24

vision

Open cv object recognition with clojure
Clojure
1
star
25

clj-avr

AVR micros related stuff
Clojure
1
star
26

ganache-test

For reporting a ganache bug
JavaScript
1
star
27

clj-scraper

Clojure DSL for scraping html pages
Clojure
1
star
28

docker-flow-storm-basic

A basic template for trying docker and remote debugging with FlowStorm
Clojure
1
star
29

stumpish-notify

Small python and lisp programs to listen to thunderbird and pidgin notifications via Dbus and call stumpwm lisp functions using stumpish to notify unread emails and conversations
Common Lisp
1
star