Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binding keys to REPL functions, passing the namespace and cursor line (Notespace integration) #863

Closed
daslu opened this issue Dec 9, 2020 · 17 comments

Comments

@daslu
Copy link

daslu commented Dec 9, 2020

Many thanks for Calva!

Background (you can skip reading this part)

In the last few weeks, a few of us started using Notespace for literate programming.
In Emacs+Cider, it is easy to interact with Notespace, as we can comfortably bind keys to Notespace API function calls, and even provide these calls with the important information of what line the cursor is in.
We want to create a more complete Notespace story for Calva as well. This brings us to the following need.

A feature request

  • We want to create a Calva keybinding that saves the file and then runs (f) in the REPL, for a pre-configured function f.
  • We want to create a Calva keybinding that saves the file and then runs (f nsp line) in the REPL, for a pre-configured function f, where nsp and line are external parameters passed by Calva, with the information of the current namespace and the current line where the cursor lies in the edited file.
@bpringe
Copy link
Member

bpringe commented Dec 9, 2020

Interesting! A PR is welcome.

@daslu
Copy link
Author

daslu commented Dec 9, 2020

@behrica
Copy link
Contributor

behrica commented Dec 10, 2020

Maye a simple approach could be that the snippets for configuring the custom commands can have some "special character sequences" which get replaced by Calva before calling the snippet:

"calva.customREPLCommandSnippets": [
        {
            "name": "print-line-number",
            "snippet": "(println $line)",
            "repl": "clj"
        },
    ]

For the case of notespace we would only need 2:
$line - current line number
$ns - current name space // true ? The notespace functions work on "*ns*"

-> a kind of editing context

https://github.com/scicloj/notespace/blob/a3758b631420d093ff2d342dd5ee3b98feba3905/emacs-config.el#L14
would become

        {
            "name": "eval-and-realize-note-at-line",
            "snippet": "(notespace.api/eval-and-realize-note-at-line $line)",
            "repl": "clj"
        },
    ]

Not sure, if something else makes sense
(current column ?, current xxx ?)

Somewho information on the current "state" of calva, which a Clojure function itself cannot get from nowhere else.

@behrica
Copy link
Contributor

behrica commented Dec 10, 2020

Not sure, if something else makes sense
(current column ?, current xxx ?)

  • current file name ?
  • cider port ?
  • cider host ?
    ...

@behrica
Copy link
Contributor

behrica commented Dec 10, 2020

It works in principle with a trivial change to Calva:

@@ -375,11 +375,13 @@ async function evaluateCustomCommandSnippetCommand(): Promise<void> {
                 saveAs: "runCustomREPLCommand"
             });
             if (pick && snippetsDict[pick] && snippetsDict[pick].snippet) {
-                const command = snippetsDict[pick].snippet;
+                var command = snippetsDict[pick].snippet;
                 const editor = vscode.window.activeTextEditor;
+                var currentLine = editor.selection.active.line
                 const editorNS = editor && editor.document && editor.document.languageId === 'clojure' ? namespace.getNamespace(editor.document) : undefined;
                 const ns = snippetsDict[pick].ns ? snippetsDict[pick].ns : editorNS;
                 const repl = snippetsDict[pick].repl ? snippetsDict[pick].repl : "clj";
+                command = command.replace("$line",currentLine);
                 await evaluateInOutputWindow(command, repl ? repl : "clj", ns);
             }
         } catch (e) {

and a custom command setting like thgis:

{
            "name": "eval-and-realize-note-at-line",
            "snippet": "(notespace.api/eval-and-realize-note-at-line $line)",
            "repl": "clj" 
        }

@behrica
Copy link
Contributor

behrica commented Dec 10, 2020

@daslu This change would be enough for all commands here:
https://github.com/scicloj/notespace/blob/master/emacs-config.el

A namespace is not really needed. But it would be as well available in variable editorNS.

Your example Emacs configuration saves as well the file.

VS code has a built-in autosave feature instead.

Both together would bring (nearly) the same notespace experience to Calva.

The custom commands can not be bound freely to keystrokes, but are all available as 'Ctrl-Alt-c 1 " 2 / 3 /...

@daslu
Copy link
Author

daslu commented Dec 12, 2020

@behrica that looks wonderful. I will try it soon.

@daslu
Copy link
Author

daslu commented Dec 14, 2020

@behrica this works beautifully.

It seems kind of analogous to the Cursive substitutions in REPL commands:
https://cursive-ide.com/userguide/repl.html

@bpringe what do you think?

@behrica
Copy link
Contributor

behrica commented Dec 14, 2020

@behrica this works beautifully.

It seems kind of analogous to the Cursive substitutions in REPL commands:
https://cursive-ide.com/userguide/repl.html

Indeed the same idea.
But Cursive does not have "line number". But we took take inspiration from there, which substitutions can be useful, more general then for notespace only

@PEZ
Copy link
Collaborator

PEZ commented Dec 17, 2020

Awesome, @behrica! Care to provide a PR with those changes? I can pick up the torch from here, but it is more fun if you PR it. 😄

I'm thinking all your suggestions are good. Maybe start with line, column, file? And ns for convenience? $1-$9 for arguments that Calva will prompt for?

@PEZ
Copy link
Collaborator

PEZ commented Dec 17, 2020

I also suspect Calva currently messes up *1-*3 for the snippets. Would be nice to be able to use those reliably too.

@behrica
Copy link
Contributor

behrica commented Dec 18, 2020

I will do an initial change request, as you proposed.
But I am an absolute beginner in Typescript / Javascript... (and Calva ...)

Do you have a preference for the "special character" to mark the text substitutions ?
"$" vs "~" ?

It should be something, which is "impossible" to be part of any normal Clojure syntax.
"$" is valid in Clojure to denote inner classes, while "~" is only valid as part of macro definitions.

So maybe "~" is even less likely to be used for an other purpose in the substitution.

I will document this as well somewhere then: https://github.com/BetterThanTomorrow/calva/blob/master/docs/site/custom-commands.md

@PEZ
Copy link
Collaborator

PEZ commented Dec 18, 2020

Christmas comes early! 🎄 Awesome that you are planning to include docs. Both @bpringe and I will be ready to answer any Calva dev questions. Extra quickly if you're on the Clojurian Slack and the #calva channel there. As for TypeScript, I'm no expert, but of course ready to help with those questions as well, should they arise. Not a total beginner, at least. 😄 Same goes for vscode. Also if you find gaps in the How to Hack on Calva stuff, please let us know. We truly want Calva to be contributor friendly.

As for special character. This is always tricky with templating, right? As for those two alternatives.

  • Would inner classes be used in snippets? (I don't know enough about it to have a guess)
  • ~ can be used outside macros, right? unquote-splicing

Maybe go for something that is not just ”impossible”. Like 0_, even if that could look a bit weird...

What I sometimes do is work Documentation first, so I write the docs and see if they make sense to me and others. Then implement. Maybe that could help here as well.

Another thing that has been bothering me regarding the snippets is that they are pretty cumbersome to write inside JSON settings. Would make more sense with an EDN file in the .calva directory. But maybe that is stuff for a separate PR. 😄

@bpringe
Copy link
Member

bpringe commented Dec 18, 2020

@daslu This looks good to me, taking into account what @PEZ said above for the special character. We definitely don't want to have to change it later because of some conflict with Clojure, because that seems like an unavoidable breaking change.

Another thing that has been bothering me regarding the snippets is that they are pretty cumbersome to write inside JSON settings. Would make more sense with an EDN file in the .calva directory. But maybe that is stuff for a separate PR.

That's a good idea, but I agree, another PR. 😄

@behrica
Copy link
Contributor

behrica commented Dec 18, 2020

Christmas comes early! Awesome that you are planning to include docs. Both @bpringe and I will be ready to answer any Calva dev questions. Extra quickly if you're on the Clojurian Slack and the #calva channel there. As for TypeScript, I'm no expert, but of course ready to help with those questions as well, should they arise. Not a total beginner, at least. Same goes for vscode. Also if you find gaps in the How to Hack on Calva stuff, please let us know. We truly want Calva to be contributor friendly.

As for special character. This is always tricky with templating, right? As for those two alternatives.

  • Would inner classes be used in snippets? (I don't know enough about it to have a guess)
  • ~ can be used outside macros, right? unquote-splicing

Maybe go for something that is not just ”impossible”. Like 0_, even if that could look a bit weird...

I propose to stick to the "$line".

In any case the subsitution sequence would be "$line", so a single "$" would not be touched.

This would mean, it only fails if somebody refers to a Java inner class which is called "line...."
This is pretty unlikely to exist.
On top it would fail with an obvious error message

Java naming conventions dictate that class names should begin with an upper case letter

@PEZ
Copy link
Collaborator

PEZ commented Dec 18, 2020

How about people using things like $ and $something for binding in as-> threading? Not sure if anything else but $ is used. In any case, this would be documented, and to instruct people to refrain from using such binding names in their snippet code is pretty OK, I think. I now vote for $line, etcetera.

@daslu
Copy link
Author

daslu commented Dec 19, 2020

Thanks for your kind help, @PEZ @bpringe @behrica .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants