-
-
Notifications
You must be signed in to change notification settings - Fork 96
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
Using racket-hash-lang-mode with org-mode source blocks #692
Comments
In
I don't use org babel stuff enough to know off the top of my head exactly what you're expecting, and how that would work. I'd be happy to take a look. Already |
I looked at this again, trying to learn more about org source block handling (with which I have almost no hands-on experience). I read the org-source docs, and skimmed some of the source code. Maybe I'm wrong, but:
So status quo I think your only choice is to include the Maybe we could stipulate some new header argument property like |
Actually from looking at some examples like Also (let ((edit-prep-func (intern (concat "org-babel-edit-prep:" lang))))
(when (fboundp edit-prep-func)
(funcall edit-prep-func babel-info))) So conceivably I could define a And either way, do something with the value:
|
I pushed to a topic branch a simple commit ddd0f46. It just adds to (defun org-babel-edit-prep:racket-hash-lang (babel-info)
"Recreate back-end hash-lang object using :shebang property.
`org-edit-src-code' calls us AFTER the `racket-hash-lang-mode'
buffer is created. So if there is a :shebang property with
\"#lang foo\", we need to recreate the back end object using the
option where we can supply this."
(pcase babel-info
(`(,_racket-hash-lang ,_contents ,props . ,_)
(when-let (shebang (cdr (assq :shebang props)))
;; re-create back end hash-lang object
(racket--hash-lang-delete)
(setq-local racket--hash-lang-id
(racket--cmd/await
nil
`(hash-lang
create
,(cl-incf racket--hash-lang-next-id)
,shebang
,(buffer-substring-no-properties (point-min) (point-max))))))))) Given an example file
I did C-c ' and "it works". I'm not sure it's quite that simple...? |
p.s. This also seems to support "DRY" patterns to avoid repeating a property like Like a property for the whole org file:
As well as a property drawer for just a section within the org file. As well as setting the Emacs Lisp variable (Again I have almost no experience with this stuff. I just read the docs and tried a quick example to confirm.) |
Greg Hendershott ***@***.***> writes:
I pushed to a topic branch a simple commit ddd0f46. It just adds to `racket-hash-lang.el`:
```emacs-lisp
(defun org-babel-edit-prep:racket-hash-lang (babel-info)
"Recreate back-end hash-lang object using :shebang property.
I tried copying this function to my init.el and as you say it seems to
work for editing. At least indentation seems to work. For syntax
highlighting as you mention in the documentation there is strings,
numbers and symbols, so it is pretty muted (without additional
configuration).
Tangling does not work yet, but I suspect this is unrelated to the
original question; I had the same error when enabling racket-hash-lang
mode globally.
When I try to tangle (C-c C-v C-t) with
```org-mode
#+begin_src racket-hash-lang :shebang "#lang rhombus" :tangle foo.rkt
"string"
~string
1 + 1
#+end_src
```
I get
```
mapc: Buffer is read-only: #<killed buffer>
```
I could not (easily) get a backtrace, sorry. Do you want a seperate
issue for this, or just leave all the org-src+hash-lang mode discussion
here?
|
I think this is because the ;; Create back end hash-lang object.
;;
;; On the one hand, `racket--cmd/await' would be simpler to use
;; here. On the other hand, when the back end isn't running, there's
;; a delay for that to start, during which the buffer isn't
;; displayed and Emacs seems frozen. On the third hand, if we use
;; `racket--cmd/async' naively the buffer could try to interact with
;; a back end object that doesn't yet exist, and error.
;;
;; Warm bowl of porridge: Make buffer read-only and use async
;; command to create hash-lang object. Only when the response
;; arrives, i.e. the back end object is ready, enable read/write and
;; set various hook functions that depend on `racket--hash-lang-id'.
;;
;; Also, handle the back end returning nil for the create -- meaning
;; there's no sufficiently new syntax-color-lib -- by downgrading to
;; plain `prog-mode'. This works fine when the user interactively starts However I'm not sure how to balance all the competing needs here but I'll give it a think... p.s. When tracing through the code, I noticed that |
I think for now one issue makes sense. Seems like some overlap among the three things -- edit, tangle, execute. At least wrt decisions like using |
Update: I understand more about how things work. I think I see how to make both However I don't see how to make formatting work for the source block itself in the original I've looked for kludgy ways for that special buffer to discover the corresponding source block and buffer, and the metadata like the lang. So far I'm stumped. Even if I had some kludge, it might be fragile. To go with the grain of Emacs and org-mode expectations, each lang gets its own major mode, which knows about formatting the lang. Whereas the concept of A better fit would be for each hash-lang to have its own Emacs major mode. This could be a small major mode using Maybe I could make a little Emacs macro to do the |
p.s. In my previous comment I'm referring to the formatting that happens when |
More work for issue #692 As far as I can tell, org source blocks and org-babel are designed around the assumption that each language will have its own major mode. Otherwise, the source block language isn't available in all scenarios. Therefore go with the flow: Even though racket-hash-lang-mode can handle all hash-langs, people will need to derive from it a new major mode for each lang they want to use with org source blocks. A new racket-define-hash-lang macro makes this easier, as well as handling related configuration like auto-mode-alist, org-src-lang-modes, and org-babel-tangle-lang-exts. With this we (intend to) fully support org source block editing and tangling. When it comes to executing, we supply a basic org-babel-execute:<lang> function that knows how to run all hash-langs. However it only supports the :result-type output -- not values. And it does not support input :vars. In both cases, the syntax and semantics will of course vary among languages. However a user could define a org-babel-expand-body:<lang> to support :vars for a given lang. (But I don't yet have any how :result-type value would work.) One issue that comes up for all three (edit, tangle, execute) is what to do about #lang lines. A Racket program must start with exactly one. - edit: The user need not include one. We add one automatically when they C-c ' to edit in the dedicated edit buffer), to keep things like racket-xp-mode happy. And we subtract it when writing back to the org buffer. - execute: We add one if the block lacks one. - tnagle: It's up to the user to start the /first/ block (for each lang) with one, but no others.
Closes issue #692 As far as I can tell, org source blocks and org-babel are designed around the assumption that each language will have its own major mode. Otherwise, the source block language isn't available in all scenarios. Therefore go with the flow: Even though racket-hash-lang-mode can handle all hash-langs, people will need to derive from it a new major mode for each lang they want to use with org source blocks. A new racket-define-hash-lang macro makes this easier, as well as handling related configuration like auto-mode-alist, org-src-lang-modes, and org-babel-tangle-lang-exts. With this we (intend to) fully support org source block formatting, editing, and tangling. When it comes to executing, we supply a basic org-babel-execute:<lang> function that knows how to run all hash-langs. However it only supports the :result-type output -- not values. And it does not support input :vars. In both cases, the syntax and semantics will of course vary among languages. However a user could define a org-babel-expand-body:<lang> to support :vars for a given lang. (But I don't yet have any how :result-type value would work.) One issue that comes up for all four scenarios is what to do about lang lines -- a Racket program must start with exactly one. 1. format: We use the back end hash-lang option to set the lang separately (as we also use for the REPL). 2. edit: The user need not include one. We add one automatically when they C-c ' to edit in the dedicated edit buffer), to keep things like racket-xp-mode happy. And we subtract it when writing back to the org buffer. 3. execute: We add one if the block lacks one. 4. tangle: It's up to the user to start the /first/ block (for each lang) with one, but not the remainder.
Closes issue #692 As far as I can tell, org source blocks and org-babel are designed around the assumption that each language will have its own major mode. Otherwise, the source block language isn't available in all scenarios. Therefore go with the flow: Even though racket-hash-lang-mode can handle all hash-langs, people will need to derive from it a new major mode for each lang they want to use with org source blocks. A new racket-define-hash-lang macro makes this easier, as well as handling related configuration like auto-mode-alist, org-src-lang-modes, and org-babel-tangle-lang-exts. With this we (intend to) fully support org source block formatting, editing, and tangling. When it comes to executing, we supply a basic org-babel-execute:<lang> function that knows how to run all hash-langs. However it only supports the :result-type output -- not values. And it does not support input :vars. In both cases, the syntax and semantics will of course vary among languages. However a user could define a org-babel-expand-body:<lang> to support :vars for a given lang. (But I don't yet have any how :result-type value would work.) One issue that comes up for all four scenarios is what to do about lang lines -- a Racket program must start with exactly one. 1. format: We use the back end hash-lang option to set the lang separately (as we also use for the REPL). 2. edit: The user need not include one. We add one automatically when they C-c ' to edit in the dedicated edit buffer), to keep things like racket-xp-mode happy. And we subtract it when writing back to the org buffer. 3. execute: We add one if the block lacks one. 4. tangle: It's up to the user to start the /first/ block (for each lang) with one, but not the remainder.
OK this took awhile but I think I understand the problem space now. I have a solution that I believe is generally correct. (Caveat: Although I've tried to think of all the scenarios and edge cases, I've probably overlooked some.) With commit 4491cc0 you get a In your original example, you can Copy of the commit message:
What do you think about the idea? If you're able to try the commit from that branch, how does it work for you? |
Greg Hendershott ***@***.***> writes:
OK this took awhile but I think I understand the problem space now.
I have a solution that I believe is generally correct. (Caveat: Although I've tried to think of all the scenarios and edge cases, I've probably overlooked some.)
With commit 4491cc0 you get a `racket-define-hash-lang` macro.
Apologies for the slow reply. I think the macro is actually called
racket-declare-hash-lang-for-org-babel ?
In your original example, you can `(racket-define-hash-lang smol
".smol")` and all of {format, edit, tangle, execute} should "just
work" when you use `smol` as the language for org source blocks.
I tried this with
```
(when (require 'racket-hash-lang nil t)
(racket-declare-hash-lang-for-org-babel datalog ".dlog"))
```
and
```
#+begin_src datalog :tangle lecture15/borrows1.dlog
borrows(java, cpp).
borrows(cpp, c).
borrows(c, bcpl).
borrows(pascal, algol).
descends(A, B) :- borrows(A, B).
descends(A, B) :- borrows(A, Z), descends(Z, B).
#+end_src
```
Editing seems OK, but
- somehow the tangled file does not have a "#lang"
line. Is that expected?
- Would it make sense to update auto-mode-alist to open files with the
given suffix in the newly created mode? Maybe that is mission creep,
I'm not sure.
|
Yes, sorry about the name change. Originally I thought this might be relevant beyond org-babel, but then realized not, and decided to make the name more specific to reflect that.
Similarly, originally I had the macro do exactly that, but took it out. For a few real-world langs -- like
Yes. I thought I had it in the doc string, but, you need to do the |
Greg Hendershott ***@***.***> writes:
> Would it make sense to update auto-mode-alist to open files with the
> given suffix in the newly created mode? Maybe that is mission creep,
> I'm not sure.
Similarly, originally I had the macro do exactly that, but took it out.
For a few real-world langs -- like `racket`, `scribble`, and `rhombus` -- there are langs people might prefer to use with a dedicated "classic" mode (like `racket-mode`, `scribble-mode`, or `rhombus-mode`) as opposed to `racket-hash-lang-mode`. So I think it is mission creep.
That makes sense, but I then I wonder (lazily without looking at the
code) what the file extension is needed for?
> somehow the tangled file does not have a "#lang"
> line. Is that expected?
Yes. I thought I had it in the doc string, but, you need to do the
`#lang` line explicitly in the _first_ block. org-tangle is just
concatenating all those.
It also works to add :shebang to any block. As you
observed in some previous email that has side effects with respect to
permissions, but I just override that globally with something like
#+PROPERTY: header-args :tangle-mode (identity #o644)
I have not noticed any problems with abusing :shebang in this way. I use
it quite extensively for tangling racket files (with classic racket-mode).
|
It's used to add the lang to IIUC so in the scenario where you tangle
Although it's been a few weeks now, I recall from code spelunking that Given that, it's going to be up to the user to have the tangled output start with a |
Greg Hendershott ***@***.***> writes:
> That makes sense, but I then I wonder (lazily without looking at the
> code) what the file extension is needed for?
It's used to add the lang to `org-babel-tangle-lang-exts`.
IIUC so in the scenario where you tangle `foo.org` but don't supply a filename it will just do `foo.<ext>`?
I didn't know about it, but apparently this is a thing some
people like to do.
Given that, it's going to be up to the user to have the tangled output
start with a `#lang` line... somehow -- either via the `:shebang`
property, or, by including the `#lang` line literally in the first
block. (I feel like the latter is simpler for me to document, which
is what I did -- but I didn't mean to imply the former can't work or
that you shouldn't prefer it.)
Maybe I will send a doc patch later. It's obviously not a blocker.
I do notice some strange behaviour with <TAB> in a source block when not
in an indirect buffer. I haven't got a good reproducer for that yet.
|
I pushed another commit with some doc prose edits, to the issue-692 branch. The doc string now: (defmacro racket-declare-hash-lang-for-org-babel (lang ext)
"Arrange for a Racket hash-lang to work with org-babel.
LANG should be an unquoted symbol, same as you would use in a
Racket =#lang= line.
EXT should be a string with the file extension for LANG, /not/
including any dot.
Examples:
(racket-define-hash-lang rhombus \"rhm\")
(racket-define-hash-lang scribble/manual \"scrbl\")
This macro will:
0. Define a major mode derived from `racket-hash-lang-mode' named
`racket-hash-lang:LANG-mode'.
1. Add the language to `org-src-lang-modes' and
`org-babel-tangle-lang-exts'.
2. Define a org-babel-edit-prep:LANG function.
3. Define a org-babel-execute:LANG function, which delegates to
`racket--hash-lang-org-babel-execute'. See its doc string for
more information -- including why this macro /cannot/ also
define a org-babel-expand-body:LANG function.
4. Allow a buffer to omit the explicit #lang line, when it is
created by `org-mode' for user editing or formatting of a
source code block whose language property is LANG.
Discussion:
A valid Racket program consists of one outermost module per
source file, using one lang. Typically this is expressed using a
=#lang= line -- which must occur exactly once at the start of the
file. In such a buffer, `racket-hash-lang-mode' \"just works\".
When using multiple `org-mode' source blocks of the same lang,
the situation is trickier:
- Although you could start /every/ block with a lang line, that's
tedious, and org-tangle will concatenate them into an invalid
program.
- On the other hand, if you start only the /first/ block with a
lang line, then various org-babel features won't work properly
with the subsequent blocks. Basically this is because org
creates a hidden buffer using `racket-hash-lang-mode', but the
source block's lang property value is not available to that
buffer, so it can't know what lang line to add automatically.
- Similarly, if you use the :shebang property to tangle
correctly, that property value is not available in the hidden
buffers created by org mode.
TL;DR: Org assumes that each lang will have a major mode that
knows enough to do what is required. To accommodate this it is
simplest to define a distinct major mode for each org source
block language." Unfortunately I think that prose is still not great about explaining that shebang is another good/sufficient way to make tangling work. Most of the discussion (attempts to) explain that org-mode creates hidden buffers, and those buffers get no access to any of these src block properties (source lang, shebang, whatever). That's what pushes us to derive a major mode for each source lang. (The whole situation is kind of confusing. I want to make sure I understand it, and also try to make users not need to understand it more than necessary. So the macro tries to help do that, but the doc string still needs to explain the situation just in case... ugh.) |
Greg Hendershott ***@***.***> writes:
I pushed another commit with some doc prose edits, to the [issue-692 branch](https://github.com/greghendershott/racket-mode/commits/issue-692/).
The doc string now:
```elisp
(defmacro racket-declare-hash-lang-for-org-babel (lang ext)
"Arrange for a Racket hash-lang to work with org-babel.
LANG should be an unquoted symbol, same as you would use in a
Racket =#lang= line.
Maybe it would be enough to mention that this "work with org-babel" is
really about source block editing, and for tangling the user should use
the same method as works for them with racket-mode classic?
At some point a FAQ entry (or similar) about tangling might be
appropriate, but it doesn't seem to be racket-hash-lang-mode specific?
|
Closes issue #692 As far as I can tell, org source blocks and org-babel are designed around the assumption that each language will have its own major mode. Otherwise, the source block language isn't available in all scenarios. Therefore go with the flow: Even though racket-hash-lang-mode can handle all hash-langs, people will need to derive from it a new major mode for each lang they want to use with org source blocks. A new racket-define-hash-lang macro makes this easier, as well as handling related configuration like auto-mode-alist, org-src-lang-modes, and org-babel-tangle-lang-exts. With this we (intend to) fully support org source block formatting, editing, and tangling. When it comes to executing, we supply a basic org-babel-execute:<lang> function that knows how to run all hash-langs. However it only supports the :result-type output -- not values. And it does not support input :vars. In both cases, the syntax and semantics will of course vary among languages. However a user could define a org-babel-expand-body:<lang> to support :vars for a given lang. (But I don't yet have any how :result-type value would work.) One issue that comes up for all four scenarios is what to do about lang lines -- a Racket program must start with exactly one. 1. format: We use the back end hash-lang option to set the lang separately (as we also use for the REPL). 2. edit: The user need not include one. We add one automatically when they C-c ' to edit in the dedicated edit buffer), to keep things like racket-xp-mode happy. And we subtract it when writing back to the org buffer. 3. execute: We add one if the block lacks one. 4. tangle: It's up to the user to start the /first/ block (for each lang) with one, but not the remainder.
I'd like to be able to set a variable to tell
racket-hash-lang-mode
what the buffer syntax is. My use case is editing source blocks in org-mode where the #lang is implicit. Source might look like the followingI can configure this to translate
smol
toracket-hash-lang-mode
when running org-edit-special (and it works without a file, thanks for that). I'm just experimenting with racket-hash-lang-mode, so I have a hard time seeing what is lost by not actually knowing the#lang
, but I guess it must be something, right?The text was updated successfully, but these errors were encountered: