Skip to main content

Lisp Code schreiben mit ChatGPT

Das war einfach!

Ich bin gerade dabei, den Besuch eines großen Festivals zu organisieren, mehr als 175 Bands spielen an 4 Tagen und etwa einem Dutzend verschiedener Orte, die quer über die Stadt verteilt sind. Das ganze ist eine logistische Herausforderung. Welche Bands möchte ich sehen und spielen die mit ausreichend zeitlichem Abstand um von Location A in Halle B zu kommen?

Also habe ich mir von der Seite des Veranstalters alle Daten in ein Org Dokument kopiert und mir eine Übersicht generieren lassen. in Org sieht das so aus:

* Bands  
   :PROPERTIES:
   :COLUMNS: %ITEM %Ort %Datum %Uhrzeit %Link1 %Link2 %Genre
   :END:

#+BEGIN: columnview :id local :skip-empty-rows t
| ITEM             | Ort                     |      Datum | Uhrzeit | Link1 | Link2 | Genre |
|------------------+-------------------------+------------+---------+-------+-------+-------|
| 12 Illusions (D) | Moritzbastei            | 17.05.2024 |   21.00 | [[https://www.youtube.com/watch?v=MOPkUBQbCzA][Song1]] | [[https://www.youtube.com/watch?v=_CUVz7neaRM][Song2]] | o     |
| A Projection (S) | Volkspalast Kuppelhalle | 19.05.2024 |   17.40 | [[https://www.youtube.com/watch?v=W7CndC-CQrw][Song1]] | [[https://www.youtube.com/watch?v=D1C5KpP3Hic][Song2]] | o     |
#+END:   

** 12 Illusions (D)
   :PROPERTIES:
   :Datum: 17.05.2024
   :Uhrzeit: 21.00
   :Ort: Moritzbastei
   :Adresse: Universitätsstraße 9
   :Strassenbahn: 9 (Roßplatz)
   :Strassenbahn+: 8, 9, 11, 14, 16, 32 (Wilhelm-Leuschner-Platz)
   :Bus: 412 (Roßplatz)
   :Bus+: N8 (Roßplatz)
   :S-Bahn: S1, S2, S3, S4, S5, S5X, S6 (Wilhelm-Leuscher-Platz)
   :Link1: [[https://www.youtube.com/watch?v=MOPkUBQbCzA][Song1]]
   :Link2: [[https://www.youtube.com/watch?v=_CUVz7neaRM][Song2]]
   :Genre: o 
   :END:

12 ILLUSIONS is a newly-launched German dark electro pop band.

** A Projection (S)
   :PROPERTIES:
   :Datum: 19.05.2024
   :Uhrzeit: 17.40
   :Ort: Volkspalast Kuppelhalle
   :Adresse: Messehalle 16 / Puschstraße 10
   :Strassenbahn: 16 (An den Tierkliniken)
   :Link1: [[https://www.youtube.com/watch?v=W7CndC-CQrw][Song1]]
   :Link2: [[https://www.youtube.com/watch?v=D1C5KpP3Hic][Song2]]
   :Genre: o 
   :END: 

Die Tabelle oben ist meine Übersicht, und da dort mittlerweile knapp 200 Zeilen drin sind ist es mühsam, wenn man zu einer dieser Bands in den Teil mit den Details scrollen möchte. Wenn der Veranstalter die nicht schon alphabetisch aufgelistet hätte wäre es kaum machbar.

Die Übersichtstabelle generiert sich, dank Org Magie, immer neu wenn ich C-c C-c auf der Kopfzeile drücke. Die erste Spalte ist also buchstäblich identisch mit der Überschrift der einzelnen Bands, Wenn diese Überschrift einen Link auf sich selbst enthalten würde, dann wären in der ersten Spalte lauter Links auf die Details zu den Bands.

Um diese Links zu erzeugen muss ich mehrere Funktionen aufrufen:

  • Erzeugen einer Custom ID für die Überschrift
  • Kopieren dieser ID ins Clipboard
  • Markieren der Überschrift
  • Einfügen des Links

und das knapp 200 mal

Da wäre es zumindest schön, eine Funktion zu haben, die diese Schritte durchführt, und die ich dann an eine Taste binden kann.

Eine Nachfrage via Social Medie ergab, das zwar ähnliche Dinge schon erstellt wurden, aber alle hatten dann doch etwas leicht anderes zum Ziel. Und dann dachte ich mir,ich schaue mal ob ChatGPT mir das Problem lösen kann.

Der erste Versuch

Ich: Can you write a emacs lisp function, to link an orgmode Heading at point with its own id, creating one if it does not already exist?

ChatGPT: Certainly! Below is a simple Emacs Lisp function that links an Org mode heading at point with its own ID, creating one if it does not already exist:

(defun link-heading-with-id ()
  "Link an Org mode heading at point with its own ID, creating one if it does not already exist."
  (interactive)
  (org-back-to-heading)
  (let* ((heading (nth 4 (org-heading-components)))
         (id (org-entry-get nil "CUSTOM_ID")))
    (if (not id)
        (progn
          (setq id (concat "id-" (md5 heading)))
          (org-set-property "CUSTOM_ID" id)))
    (insert (format "[[#%s][%s]]" id heading))))

Nicht schlecht für den ersten Versuch. Die Funktion bewegt zuerst den Cursor an den Anfang der aktuellen Überschrift, ruft dann den Text der Überschrift und deren vorhandene ID ab, falls eine vorhanden ist. Falls die Überschrift keine ID hat, generiert sie eine mittels des md5-Hashes des Überschriftentextes. Schließlich fügt sie einen Link zur Überschrift mit ihrer ID ein.

Leider wird der Cursor an den Anfang der Überschrift bewegt, wodurch der neue Link vor der '** Überschrift' eingefügt wird, und sie somit keine Überschrift mehr ist. Aber vielleicht kann ChatGPF das korrigieren.

Can you alter it, so it replaces the original Heading?

ChatGPT: Certainly! Here's the modified function that replaces the original heading with the linked version:

(defun link-heading-with-id ()
  "Replace an Org mode heading at point with a linked version using its own ID, creating one if it does not already exist."
  (interactive)
  (org-back-to-heading)
  (let* ((heading-start (point))
         (heading-end (line-end-position))
         (heading (nth 4 (org-heading-components)))
         (id (org-entry-get nil "CUSTOM_ID")))
    (if (not id)
        (progn
          (setq id (concat "id-" (md5 heading)))
          (org-set-property "CUSTOM_ID" id)))
    (delete-region heading-start heading-end)
    (insert (format "* [[#%s][%s]]" id heading))))

Diese Version der Funktion löscht die originale Überschrift und fügt die verlinkte Version an ihrer Stelle ein. Aber sie hat immer noch ein Problem, das man in der letzten Zeile sehen kann. Es fügt immer eine Überschrift der Ebene 1 ein (es fügt ein Sternchen voran, immer nur eines). Mach das weg, ChatGPT!

Alter it, so that it preserves the Heading-Level

ChatGPT: Sure! Here's the modified function that preserves the heading level while replacing the original heading with the linked version:

(defun link-heading-with-id ()
  "Replace an Org mode heading at point
   with a linked version using its own ID,
   preserving the heading level
   and creating one if it does not already exist."
        (interactive)
        (org-back-to-heading)
        (let* ((heading-level (org-outline-level))
               (heading-start (point))
               (heading-end (line-end-position))
               (heading (nth 4 (org-heading-components)))
               (id (org-entry-get nil "CUSTOM_ID")))
          (if (not id)
              (progn
                (setq id (concat "id-" (md5 heading)))
                (org-set-property "CUSTOM_ID" id)))
          (delete-region heading-start heading-end)
          (insert (format "%s [[#%s][%s]]"
                          (make-string heading-level ?*)
                          id heading))))

Diese Version der Funktion ruft zunächst die Überschriftsebene mit org-outline-level ab. Dann löscht sie die ursprüngliche Überschrift und fügt die verlinkte Version mit derselben Überschriftsebene ein. Ich habe es ausprobiert, und es hat wie beabsichtigt funktioniert.

* Bands  
   :PROPERTIES:
   :COLUMNS: %ITEM %Ort %Datum %Uhrzeit %Link1 %Link2 %Genre
   :END:

#+BEGIN: columnview :id local :skip-empty-rows t
| ITEM                                                       | Ort          |      Datum | Uhrzeit | Link1 | Link2 | Genre |
|------------------------------------------------------------+--------------+------------+---------+-------+-------+-------|
| [[#id-e9884ccd6b1210ce39f04bc2eb7f108a][12 Illusions (D)]] | Moritzbastei | 17.05.2024 |   21.00 | Song1 | Song2 | o     |
#+END:   

** [[#id-e9884ccd6b1210ce39f04bc2eb7f108a][12 Illusions (D)]]
   :PROPERTIES:
   :Datum: 17.05.2024
   :Uhrzeit: 21.00
   :Ort: Moritzbastei
   :Adresse: Universitätsstraße 9
   :Strassenbahn: 9 (Roßplatz)
   :Strassenbahn+: 8, 9, 11, 14, 16, 32 (Wilhelm-Leuschner-Platz)
   :Bus: 412 (Roßplatz)
   :Bus+: N8 (Roßplatz)
   :S-Bahn: S1, S2, S3, S4, S5, S5X, S6 (Wilhelm-Leuscher-Platz)
   :Link1: [[https://www.youtube.com/watch?v=MOPkUBQbCzA][Song1]]
   :Link2: [[https://www.youtube.com/watch?v=_CUVz7neaRM][Song2]]
   :Genre: o
   :CUSTOM_ID: id-e9884ccd6b1210ce39f04bc2eb7f108a
   :END:

12 ILLUSIONS is a newly-launched German dark electro pop band.

Es funktioniert

Das Einzige, was noch zu tun ist, ist das Kopieren in die Konfiguration und das Binden an eine Taste. Ach ja, und ChatGPT kann das auch erledigen.

Ich: Please make a keybinding that binds M-. to this function

ChatGPT: Sure! You can bind the link-heading-with-id function to a keybinding of your choice, such as M-. . Here's how you can do it:

(global-set-key (kbd "M-.") 'link-heading-with-id)
2024-05-09