Skip to content

Commit

Permalink
Add output channel access API (#41)
Browse files Browse the repository at this point in the history
* Add output channel access API

Fixes #36

* Remove `joyride.core/get-` prefixes

Fixes #42
  • Loading branch information
PEZ authored May 9, 2022
1 parent 9673621 commit 217e81c
Show file tree
Hide file tree
Showing 17 changed files with 137 additions and 76 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,17 @@ See the [examples](./examples) for examples including:
* Calva Structural Editing enhancements
* Opening and showing project files
* Workspace activation script
* The Joyride Extension API
* The `joyride.core` namespace

## Support and feedback

You'll find us in the `#joyride` channel on the [Clojurians Slack](http://clojurians.net)

## News

* Show HN: https://news.ycombinator.com/item?id=31203024#31206003

### Twitter

Follow the [#vsjoyride](https://twitter.com/search?q=%23vsjoyride&src=typed_query&f=live) hashtag on Twitter!
33 changes: 25 additions & 8 deletions doc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ The Joyride API consist of:
* Included clojure library namespaces
1. The Joyride *Extension API*

Give the [joyride_api.cljs](../examples/.joyride/scripts/joyride_api.cljs) example a spin, why don't ya!

Please note that Joyride's *Extension API* is also available to *Joyride scripts*.

## Scripting API
Expand All @@ -28,27 +30,42 @@ the following namespaces:

#### `joyride.core`

- `*file*`: dynamic var representing the currently executing file
- `get-invoked-script`: function returning the absolute path of the invoked script when running as a script. Otherwise returns `nil`. Together with `*file*` this can be used to create a guard that avoids running certain code when you load a file in the REPL:
- `*file*`: dynamic var holding the absolute path of file where the current evaluation is taking place
- `invoked-script`: function returning the absolute path of the invoked script when running as a script. Otherwise returns `nil`. Together with `*file*` this can be used to create a guard that avoids running certain code when you load a file in the REPL:
```clojure
(when (= (joyride/get-invoked-script) joyride/*file*)
(when (= (joyride/invoked-script) joyride/*file*)
(main))
```
- `get-extension-context`: function returning the Joyride [extension context](https://code.visualstudio.com/api/references/vscode-api#ExtensionContext) object
- `extension-context`: function returning the Joyride [ExtensionContext](https://code.visualstudio.com/api/references/vscode-api#ExtensionContext) instance
- `output-channel`: function returning the Joyride [OutputChannel](https://code.visualstudio.com/api/references/vscode-api#OutputChannel) instance

NB: While using `*file*` bare works, it will probably stop working soon. Always use it from `joyride.core`, e.g.:
Here's a snippet from the [joyride_api.cljs](../examples/.joyride/scripts/joyride_api.cljs) example.

```clojure
(ns your-awesome-script
(:require [joyride.core :refer [*file*]]
...))
(:require [joyride.core :as joyride]
...)

(doto (joyride/output-channel)
(.show true)
(.append "Writing to the ")
(.appendLine "Joyride output channel.")
(.appendLine (str "Joyride extension path: "
(-> (joyride/extension-context)
.-extension
.-extensionPath)))
(.appendLine (str "joyride/*file*: " joyride/*file*))
(.appendLine (str "Invoked script: " (joyride/invoked-script)))
(.appendLine "🎉"))
```

**NB**: Currently, using bar `*file*` works. But it will probably stop working soon. Always use it from `joyride.core`.

#### promesa.core

See [promesa docs](https://cljdoc.org/d/funcool/promesa/6.0.2/doc/user-guide).

**``**: `p/->>`, `p/->`, `p/all`, `p/any`, `p/catch`, `p/chain`, `p/create`, `p/deferred`, `p/delay`, `p/do`, `p/do`, `p/done`, `p/finally`, `p/let`, `p/map`, `p/mapcat`, `p/pending`, `p/promise`, `p/promise`, `p/race`, `p/rejected`, `p/rejected`, `p/resolved`, `p/resolved`, `p/run`, `p/then`, `p/thenable`, `p/with`, and `p/wrap`
**``**: `p/->>`, `p/->`, `p/all`, `p/any`, `p/catch`, `p/chain`, `p/create`, `p/deferred`, `p/delay`, `p/do`, `p/do!`, `p/done?`, `p/finally`, `p/let`, `p/map`, `p/mapcat`, `p/pending`, `p/promise`, `p/promise?`, `p/race`, `p/rejected`, `p/rejected?`, `p/resolved`, `p/resolved?`, `p/run!`, `p/then`, `p/thenable?`, `p/with`, and `p/wrap`

### Possibly coming additions

Expand Down
4 changes: 2 additions & 2 deletions examples/.joyride/scripts/activate.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
;; Joyride extension is deactivated.
(defn- push-disposable [disposable]
(swap! !db update :disposables conj disposable)
(-> (joyride/get-extension-context)
(-> (joyride/extension-context)
.-subscriptions
(.push disposable)))

Expand All @@ -35,5 +35,5 @@
"document opened:"
(.-fileName doc))))))

(when (= (joyride/get-invoked-script) joyride/*file*)
(when (= (joyride/invoked-script) joyride/*file*)
(my-main))
2 changes: 1 addition & 1 deletion examples/.joyride/scripts/fontsize.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
(.update "editor.fontSize" pts true))
nil)

(when (= (joyride/get-invoked-script) joyride/*file*)
(when (= (joyride/invoked-script) joyride/*file*)
(set-global-fontsize 12))

;; live demo here: https://twitter.com/borkdude/status/1519709769157775360
2 changes: 1 addition & 1 deletion examples/.joyride/scripts/ignore_form.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
(p/do! (eu/delete-range! editor range-before-insert-position))
(p/do! (eu/insert-text!+ "#_" editor insert-position)))))

(when (= (joyride/get-invoked-script) joyride/*file*)
(when (= (joyride/invoked-script) joyride/*file*)
(main))
31 changes: 26 additions & 5 deletions examples/.joyride/scripts/joyride_api.cljs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns joyride-api
(:require ["vscode" :as vscode]
[promesa.core :as p]))
[promesa.core :as p]
[joyride.core :as joyride]))

(def joyrideExt (vscode/extensions.getExtension "betterthantomorrow.joyride"))
(def joyApi (.-exports joyrideExt))
Expand Down Expand Up @@ -33,10 +34,30 @@
(comment
;; Joyride scripts can also reach the Joyride extension
;; through `joyride.core`
(require '[joyride.core :as joyride])
(require '[clojure.repl :refer [doc]])
(-> (joyride/get-extension-context)
(-> (joyride/extension-context)
.-extension
.-exports)
(doc joyride/get-extension-context)
(require '[clojure.repl :refer [doc]])
(doc joyride/extension-context)
)

;; in addition to the extension context, joyride.core also has:
;; * *file* - the absolute path of the file where an
;; evaluation takes place
;; * invoked-script - the absolute path of a script being run
;; `nil` in other execution contexts
;; * output-channel - Joyride's output channel

(doto (joyride/output-channel)
(.show true)
(.append "Writing to the ")
(.appendLine "Joyride output channel.")
(.appendLine (str "Joyride extension path: "
(-> (joyride/extension-context)
.-extension
.-extensionPath)))
(.appendLine (str "joyride/*file*: " joyride/*file*))
(.appendLine (str "Invoked script: " (joyride/invoked-script)))
(.appendLine "🎉"))

;; Try both invoking this file as a script, and loading it in the REPL
2 changes: 1 addition & 1 deletion examples/.joyride/scripts/open_document.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@
"No" :no
:none)))))

(when (= (joyride/get-invoked-script) joyride/*file*)
(when (= (joyride/invoked-script) joyride/*file*)
(my-main))
2 changes: 1 addition & 1 deletion examples/.joyride/scripts/terminal.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
;; send an initial command to it
(.sendText terminal "npx nbb")))

(when (= (joyride/get-invoked-script) joyride/*file*)
(when (= (joyride/invoked-script) joyride/*file*)
(main))

;; see live demo here:
Expand Down
2 changes: 1 addition & 1 deletion examples/.joyride/scripts/webview/example.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
html (.decode (js/TextDecoder. "utf-8") data)]
(set! (.. panel -webview -html) (str html))))

(when (= (joyride/get-invoked-script) joyride/*file*)
(when (= (joyride/invoked-script) joyride/*file*)
(main))

;; live demo here: https://twitter.com/borkdude/status/1519607386218053632
8 changes: 6 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ In the `.joyride/scripts` folder you'll find mostly small examples:

A Workspace [activate.cljs] script that registers a `vscode/workspace.onDidOpenTextDocument` event handler. Demonstrates:

* Using the `joyride.core/get-extension-context` to push disposables on its `subscriptions` array. Making VS Code dispose of them when Joyride is deactivated.
* Using the `joyride.core/extension-context` to push disposables on its `subscriptions` array. Making VS Code dispose of them when Joyride is deactivated.
* A re-runnable recipe to avoid re-registering the event handler. (By disposing it and then re-register.)

## Create a Webview
Expand Down Expand Up @@ -68,10 +68,14 @@ the default Windows comment keyboard shortcut.

## Joyride API

Yes, you can script Joyride with Joyride. See the [Joyride API docs](../doc/api.md) for more about this.
Joyride comes with the `joyride.core` namespace, giving you access to things as the extension context, the Joyride output channel, and some info about the evaluation environment.

And, you can also script Joyride with Joyride using its Extension API.

Example script: [`.joyride/scripts/joyride_api.cljs`](.joyride/scripts/joyride_api.cljs)

See also: the [Joyride API docs](../doc/api.md)

## Opening a file

The [open_document.cljs](.joyride/scripts/open_document.cljs) script asks if you want to open one of the examples and then opens a random `.cljs` file from the `scripts` folder.
Expand Down
2 changes: 1 addition & 1 deletion playground/hello-joyride/.joyride/scripts/hello.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
(comment
j/*file*

(def ext-ctx (joyride/get-extension-context))
(def ext-ctx (joyride/extension-context))
(.-extensionPath ext-ctx)
)

Expand Down
25 changes: 24 additions & 1 deletion src/main/joyride/db.cljs
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
(ns joyride.db)

(defonce !app-db (atom {}))
(def init-db {:output-channel nil
:extension-context nil
:invoked-script nil
:disposables []})

(defonce !app-db (atom init-db))

(defn extension-context
"Returns the Joyride ExtensionContext instance.
See: https://code.visualstudio.com/api/references/vscode-api#ExtensionContext"
[]
(:extension-context @!app-db))

(defn invoked-script
"Returns the absolute path of the invoked script when the evaluation is made
through *Run Script*, otherwise returns `nil`."
[]
(:invoked-script @!app-db))

(defn output-channel
"Returns the Joyride OutputChannel instance.
See: https://code.visualstudio.com/api/references/vscode-api#OutputChannel"
[]
(:output-channel @!app-db))
13 changes: 7 additions & 6 deletions src/main/joyride/extension.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,16 @@

(def api (jsify {:startNReplServer start-nrepl-server+
:getContextValue (fn [k]
(when-contexts/get-context k))}))
(when-contexts/context k))}))

(defn ^:export activate [^js context]
(when context
(reset! db/!app-db {:output-channel (vscode/window.createOutputChannel "Joyride")
:extension-context context
:disposables []})
(utils/say "🟢 Joyride VS Code with Clojure. 🚗")
(p/-> (life-cycle/maybe-run-init-script+ run-user-script+
(swap! db/!app-db assoc
:output-channel (vscode/window.createOutputChannel "Joyride")
:extension-context context)
(binding [utils/*show-when-said?* true]
(utils/say "🟢 Joyride VS Code with Clojure. 🚗"))
(p/-> (life-cycle/maybe-run-init-script+ run-user-script+
(:user life-cycle/init-scripts))
(p/then
(fn [_result]
Expand Down
2 changes: 1 addition & 1 deletion src/main/joyride/nrepl.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@
(vscode/workspace.fs.delete uri)))))))

(defn server-running? []
(when-contexts/get-context ::when-contexts/joyride.isNReplServerRunning))
(when-contexts/context ::when-contexts/joyride.isNReplServerRunning))

(defn- start-server'+
"Start nRepl server. Accepts options either as JS object or Clojure map."
Expand Down
75 changes: 33 additions & 42 deletions src/main/joyride/sci.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -13,54 +13,45 @@

(def joyride-ns (sci/create-ns 'joyride.core nil))

(defn get-extension-context
"Returns the Joyride extension context object.
See: https://code.visualstudio.com/api/references/vscode-api#ExtensionContext"
[]
(:extension-context @db/!app-db))

(defn get-invoked-script
"Returns the absolute path of theinvoked script when the evaluation is made
through *Run Script*, otherwise returns `nil`."
[]
(:invoked-script @db/!app-db))

(defn ns->path [namespace]
(-> (str namespace)
(munge)
(str/replace "." "/")
(str ".cljs")))

(def !ctx (volatile!
(sci/init {:classes {'js goog/global
:allow :all}
:namespaces (assoc (:namespaces pconfig/config)
'joyride.core {'*file* sci/file
'get-extension-context (sci/copy-var get-extension-context joyride-ns)
'get-invoked-script (sci/copy-var get-invoked-script joyride-ns)})
:load-fn (fn [{:keys [namespace opts]}]
(cond
(symbol? namespace)
{:source
(let [path (ns->path namespace)]
(str
(fs/readFileSync
(path/join
(utils/workspace-root)
workspace-scripts-path
path))))}
(string? namespace) ;; node built-in or npm library
(if (= "vscode" namespace)
(do (sci/add-class! @!ctx 'vscode vscode)
(sci/add-import! @!ctx (symbol (str @sci/ns)) 'vscode (:as opts))
{:handled true})
(let [mod (js/require namespace)
ns-sym (symbol namespace)]
(sci/add-class! @!ctx ns-sym mod)
(sci/add-import! @!ctx (symbol (str @sci/ns)) ns-sym
(or (:as opts)
ns-sym))
{:handled true}))))})))
(def !ctx
(volatile!
(sci/init {:classes {'js goog/global
:allow :all}
:namespaces (assoc
(:namespaces pconfig/config)
'joyride.core {'*file* sci/file
'extension-context (sci/copy-var db/extension-context joyride-ns)
'invoked-script (sci/copy-var db/invoked-script joyride-ns)
'output-channel (sci/copy-var db/output-channel joyride-ns)})
:load-fn (fn [{:keys [namespace opts]}]
(cond
(symbol? namespace)
{:source
(let [path (ns->path namespace)]
(str
(fs/readFileSync
(path/join
(utils/workspace-root)
workspace-scripts-path
path))))}
(string? namespace) ;; node built-in or npm library
(if (= "vscode" namespace)
(do (sci/add-class! @!ctx 'vscode vscode)
(sci/add-import! @!ctx (symbol (str @sci/ns)) 'vscode (:as opts))
{:handled true})
(let [mod (js/require namespace)
ns-sym (symbol namespace)]
(sci/add-class! @!ctx ns-sym mod)
(sci/add-import! @!ctx (symbol (str @sci/ns)) ns-sym
(or (:as opts)
ns-sym))
{:handled true}))))})))

(def !last-ns (volatile! @sci/ns))

Expand Down
2 changes: 1 addition & 1 deletion src/main/joyride/utils.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
(def ^{:dynamic true
:doc "Should the Joyride output channel be revealed after `say`?
Default: `true`"}
*show-when-said?* true)
*show-when-said?* false)

(defn say [message]
(let [channel ^js (:output-channel @db/!app-db)]
Expand Down
4 changes: 2 additions & 2 deletions src/main/joyride/when_contexts.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
(swap! !db assoc-in [:contexts k] v)
(vscode/commands.executeCommand "setContext" (name k) v))

(defn get-context [k]
(defn context [k]
(get-in @!db [:contexts (if (string? k)
(keyword (str "joyride.when-contexts/" k))
k)]))

(comment
(get-context "joyride.isActive"))
(context "joyride.isActive"))

0 comments on commit 217e81c

Please sign in to comment.