{"id":18081897,"url":"https://github.com/vindarel/lem-init","last_synced_at":"2026-01-17T03:47:13.579Z","repository":{"id":175574328,"uuid":"654081554","full_name":"vindarel/lem-init","owner":"vindarel","description":"My Lem editor's musings","archived":false,"fork":false,"pushed_at":"2024-08-28T22:42:16.000Z","size":41,"stargazers_count":10,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-12T04:17:09.648Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vindarel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-15T10:52:12.000Z","updated_at":"2024-08-28T22:42:19.000Z","dependencies_parsed_at":"2024-01-03T01:39:47.475Z","dependency_job_id":"670cf11a-10d6-42dc-9ea0-f5d9bf30d14d","html_url":"https://github.com/vindarel/lem-init","commit_stats":null,"previous_names":["vindarel/lem-init"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Flem-init","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Flem-init/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Flem-init/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vindarel%2Flem-init/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vindarel","download_url":"https://codeload.github.com/vindarel/lem-init/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247411241,"owners_count":20934650,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-31T13:17:09.253Z","updated_at":"2026-01-17T03:47:13.549Z","avatar_url":"https://github.com/vindarel.png","language":"Common Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":"# My Lem init file\n\u003e For Lem 2.0\n\nWritten in literate programing with [erudite](https://github.com/mmontone/erudite) (see below).\n\n* new website: https://lem-project.github.io/\n* all keys: https://lem-project.github.io/usage/keybindings/ (missing some modes, like Lisp mode)\n\nImpressions:\n* Lem has a shitload of features! paredit, tabs, treeview, tetris…\n* very nice to discover commands in Lem itself.\n* very nice for CL, only a few keys missing. Advanced debugger.\n* **Lem has a new git interface!** that I started developping. See changes, stage a file, the hunk of a diff, interactive rebase for the best scenario… cool but needs loads of work.\n\nInstallation:\ncopy or symlink to ~/.config/lem/init.lisp (since Lem 2.2) or ~/.lem/init.lisp.\n\nI start Lem like this:\n```\n$ cd lem/\n CL_SOURCE_REGISTRY=$(pwd)// ros -s lem-sdl2 -e '(progn (lem:lem) (uiop:quit))'\n```\n\n## The keys I miss so far\n\n* M-j newline with comment (if inside comment)\n* marks (m \u003cletter\u003e and '\u003cletter\u003e (quote))\n  * as of 2025 there is now `mark-set` (C-space) and `exchange-point-mark` (C-x C-x)\n* (fixed) M-h select paragraph\n  * since Jan, 2025, there are paragraphs objects in vi-mode, we can do \"vip\" to select the current paragraph, and delete it.\n* (fixed) imenu functionality =\u003e see very poor but useful snippet below. See M-x detective-all, doing alright.\n* (fixed) C-x C-j to open directory mode on the current file =\u003e added upstream.\n* (fixed) project-aware commands =\u003e the basics added upstream.\n\nIn Lisp mode:\n * C-c C-y call function at point in the REPL, with package prefix.\n * little issue with highlighting of parens in vi insert mode, on the last paren.\n * (fixed) C-~ sync file package with REPL =\u003e done in main, \u003c2024-07-30\u003e\n * (fixed) REPL: C-c C-p go to previous prompt =\u003e done in main (end of June, 2023) (it's C-c p)\n * (fixed) Inspecting literal objects =\u003e done in main (end of June, 2023)\n\nIn vi-mode:\n* OK now, I had to send some keys upstream, there have been many more contributions, the vi-mode is now very complete to my taste.\n\nIssues in Lem 2.0:\n* (fixed!) I can't type backslash or any key with Alt Gr (right Alt key), such as ~, and I can't have my undo on C-_ =\u003e fixed upstream =\u003e un-fixed so it works best for more people. =\u003e fixed again, somewhere 2024.\n\n## Things than Lem does better than Emacs\n\n* default auto-completion UI (but not the algorithm yet)\n* ease of development for CL, commands discovery\n* sorting files by name, mtime AND by size (with human-readable format) in directory-mode\n* interactive markdown mode with multiple-major-modes (evaluate Lisp or C code inside code blocks), interactive markdown preview in a browser built-in\n\n## Load more CL libraries\nWe want some more CL libraries.\n\n```lisp\n(pushnew \"/path/to/lisp/project/\" asdf:*central-registry* :test #'equal)\n\n```\n## Starting up\nStart this config in Lem's CL package\n\n```lisp\n(in-package :lem-user)\n\n```\nEvaluating Lisp in M-: we want to be in Lem' package.\n\n```lisp\n(lem-lisp-mode/internal::lisp-set-package \"LEM\")\n\n```\nStart in vi-mode\n\n```lisp\n(lem-vi-mode:vi-mode)\n\n```\nStart the Lisp REPL in vi insert mode (commit 23-8-8)\n(add-hook lem-lisp-mode:*lisp-repl-mode-hook* 'lem-vi-mode/commands:vi-insert)\n\n## Some helper functions, bound to keys below.\nI want quick movement functions to go to the previous or next definition (function, or anything really).\nLem has beginning- and end-of-defun, but a repetitive call doesn't go outside the function definition.\n\n```lisp\n(define-command beginning-of-defun-on-function (n) (:universal)\n  \"Go to the beginning of defun, the point on the function name.\"\n  ; if n \u003c 0, go to end of defun.\n  (previous-line)\n  (lem/language-mode::beginning-of-defun-1 n)\n  (lem-vi-mode/word:forward-word-end (current-point) n t)\n  (skip-whitespace-forward (current-point) t))\n\n(define-key lem-vi-mode:*normal-keymap*\n  \"g a\"\n  'beginning-of-defun-on-function)\n```\n'lem/detective:detective-search)  ;; \u003c2023-07-05 Wed\u003e detective doesn't currently work with define-command and define-key definitions.\n\n```lisp\n\n(define-command end-of-defun-on-function (n) (:universal)\n  \"Go to the next defun, the point on the function name.\"\n  (lem/language-mode::end-of-defun n)\n  (search-forward-regexp (current-point) \"^\\\\(\")\n  (lem-vi-mode/word:forward-word-end (current-point) n t)\n  (skip-whitespace-forward (current-point) t))\n\n(define-key lem-vi-mode:*normal-keymap*\n  \"g e\"\n  'end-of-defun-on-function)\n\n```\n\u003c2023-05-25 Thu\u003e\nvi-mode doesn't have \"+\" and \"-\" keys =\u003e sent upstream.\n\ndev note: we could find the variables and command names easily thanks to Lem's autocompletion.\n\n```lisp\n(define-key lem-vi-mode:*normal-keymap*\n  \"-\"\n  'lem-vi-mode/commands:vi-previous-line)\n\n(define-key lem-vi-mode:*normal-keymap*\n  \"+\"\n  'lem-vi-mode/commands:vi-next-line)\n\n(define-key lem-vi-mode:*normal-keymap*\n  \"q\"\n  'kill-buffer)\n\n(define-key lem-vi-mode:*normal-keymap*\n  \"Space\"\n  'lem-vi-mode/commands:vi-forward-char)\n\n(define-key lem-vi-mode:*normal-keymap*\n  \"(\"\n  'backward-paragraph)\n\n```\nsent upstream.\n\n```lisp\n(define-key lem-vi-mode:*normal-keymap*\n  \")\"\n  'forward-paragraph)\n\n```\n## Abbrev completion\n\nTIL Lem has completion with abbrev.\nC-p is bound to abbrev\nAs suggested by its example, we define C-n to display a list of suggestions.\nIt is originally bound to next-line, in vi-mode too.\n\n```lisp\n(define-key lem-vi-mode:*insert-keymap* \"C-n\" 'lem/abbrev:abbrev-with-pop-up-window)\n(define-key *global-keymap* \"C-n\" 'lem/abbrev:abbrev-with-pop-up-window)\n\n```\n\n## More keys of my liking (bépo keybboard)\n\nMost make sense for a bépo keyboard only.\n\n```lisp\n(define-key lem-vi-mode:*normal-keymap*\n  \"' b\"\n  'select-buffer)\n\n```\nswitch buffers\n\n```lisp\n(define-key lem-vi-mode:*normal-keymap*\n  \"' «\"\n  'previous-buffer)\n\n(define-key lem-vi-mode:*normal-keymap*\n  \"' »\"\n  'next-buffer)\n\n```\ngo to end of buffer\n\n```lisp\n(define-key lem-vi-mode:*normal-keymap*\n  \"\u003e\"\n  'move-to-end-of-buffer)\n\n\n(define-key lem-vi-mode:*normal-keymap*\n  \"\u003c\"\n  'move-to-beginning-of-buffer)\n\n(define-key lem-vi-mode:*normal-keymap*\n  \"M-»\"\n  'move-to-end-of-buffer)\n\n(define-key lem-vi-mode:*normal-keymap*\n  \"M-«\"\n  'move-to-beginning-of-buffer)\n\n```\nvi visual mode\n\n```lisp\n(define-key lem-vi-mode/visual::*visual-keymap*\n  \"x\"\n  'lem:kill-region)\n\n(define-key *global-keymap*\n  \"C-x \\\"\"\n  'lem-core/commands/window:delete-other-windows)\n\n(define-key *global-keymap*\n  \"C-x »\"\n  'lem-core/commands/window:split-active-window-horizontally)\n\n(define-key *global-keymap*\n  \"C-x «\"\n  'lem-core/commands/window:split-active-window-vertically)\n\n(define-key *global-keymap*\n  \"F6\"\n  'lem/language-mode::comment-or-uncomment-region)\n\n(define-key *global-keymap*\n  \"M-s\"\n  'previous-line)\n(define-key *global-keymap*\n  \"M-t\"\n  'next-line)\n\n```\ndon't work or not in the terminal?\n\n```lisp\n(define-key *global-keymap* \"C-PageDown\"\n  'lem/frame-multiplexer:frame-multiplexer-next)\n\n(define-key *global-keymap* \"C-PageUp\"\n  'lem/frame-multiplexer:frame-multiplexer-prev)\n\n```\nyes:\n\n```lisp\n(define-key lem-vi-mode:*normal-keymap* \"C-n\" 'lem/frame-multiplexer:frame-multiplexer-next)\n(define-key lem-vi-mode:*normal-keymap* \"C-p\" 'lem/frame-multiplexer:frame-multiplexer-prev)\n```\nnope?\n\n```lisp\n(define-key *global-keymap* \"C-p\" 'lem/frame-multiplexer:frame-multiplexer-prev)\n(define-key *global-keymap* \"C-n\" 'lem/frame-multiplexer:frame-multiplexer-next)\n\n```\nUndo:\nthis doesn't work, it understands C-space.\n\n```lisp\n(define-key *global-keymap*\n  \"C-_\"\n  'undo)\n\n```\n## imenu\nA very poor man's imenu.\n\n```lisp\n(defun buffer-headings (txt)\n  (loop for line in (str:lines txt)\n      for parts = (str:split \" \" line)\n      for i = 1 then (incf i)\n      if (str:starts-with-p \"(def\" line)\n      ; collect (list line i))\n        collect line))\n\n(defun prompt-for-heading ()\n  (let ((candidates (buffer-headings (buffer-text (current-buffer)))))\n    (if candidates\n        (prompt-for-string \"Heading: \"\n                            :history-symbol '*imenu*\n                            :completion-function (lambda (x)\n                                                   (completion-strings x candidates))\n                            :test-function (lambda (name)\n                                             (member name candidates :test #'string=))))))\n\n(define-command imenu () ()\n  (let ((candidate (prompt-for-heading)))\n    (move-to-beginning-of-buffer)\n    (search-forward (current-point) candidate)\n    (message \"~a\" candidate)))\n\n(define-key *global-keymap* \"C-x i\" 'imenu)\n\n```\n\n## Misc\n\n```lisp\n(define-command open-init-file () ()\n  ; thx @sasanidas\n  (lem:find-file\n   (merge-pathnames \"init.lisp\" (lem-home))))\n\n(define-command oif () ()\n  (open-init-file))\n\n```\n### Open PDF files with an external program\n\n```lisp\n\n(defmethod lem-core/commands/file:execute-find-file :around (executor mode pathname)\n  (if (find (pathname-type pathname) '(\"pdf\" \"mp4\" \".mp4\") :test #'equal)\n      (open-external-file pathname)\n      (call-next-method)))\n\n```\n### Transparent background! ^^\n\n```lisp\n; (sdl2-ffi.functions:sdl-set-window-opacity (lem-sdl2/display::display-window lem-sdl2/display::*display*) 0.9)\n#+lem-sdl2\n;(sdl2-ffi.functions:sdl-set-window-opacity (lem-sdl2/display::display-window lem-sdl2/display::*display*) 1.0)\n\n```\nI want to see my logs on the terminal output:\n\n```lisp\n(log:config :info)\n\n```\n### Insert a file name (with completion)\n\n```lisp\n\n(define-command insert-file-name (filename) ((:file \"Insert file name:\"))\n  \"Inserts a filename at point.\"\n  (insert-string (current-point) filename))\n\n\n```\n### Configure legit\nLegit is now included and loaded by default.\nI only set one parameter: don't prompt for confirmation when aborting a commit message.\n\n```lisp\n\n(setf (config :prompt-for-commit-abort) nil)\n\n```\nFix a slow down for me, see\nhttps://github.com/lem-project/lem/issues/1092\nThis crashes Lem: (so, keep a patch in my local git…)\n\n```lisp\n\n; (defun my/lem-sdl2--call-with-renderer (function)\n;   (uiop:format! t \"~%running Lem with my SDL2 slowdown fix, issue 1092…~%\")\n;   (bt:with-recursive-lock-held ((display-mutex *display*))\n;     (funcall function)))\n\n```\n(setf (symbol-function 'lem-sdl2::call-with-renderer) #'my/lem-sdl2--call-with-renderer)\n### Suspend ncurses Lem (C-z)\n\n```lisp\n\n#+lem-ncurses\n(define-command suspend-lem () ()\n  ; @fukamachi\n  ; https://github.com/lem-project/lem/issues/306\n  (when (find-package 'charms/ll)\n    (uiop:symbol-call 'charms/ll 'endwin)  ; the package doesn't exist in the SDL version.\n    (sb-posix:kill (sb-posix:getpid) sb-posix:sigtstp)))\n\n(define-key *global-keymap* \"C-z C-z\" 'suspend-lem)\n\n```\n### Other settings - timestamps\nLoad a utility from another file, too short for a PR:\n\n```lisp\n(load \"~/dotfiles/lem/time-stamp.lisp\")\n\n```\nNow you can do `M-x time-stamp` to print the timestamp of the day, in the org-mode format:\n\"\u003c2023-07-05 Wed\u003e\"\n### Choose the position of the completion prompt (new in May, 2024)\n\n```lisp\n\n;(setf lem-core::*default-prompt-gravity* :bottom-display)\n;(setf lem/prompt-window::*prompt-completion-window-gravity* :horizontally-above-window)\n;(setf lem/prompt-window::*fill-width* t)\n\n```\nand show the completion list directly, without a first press on TAB:\n\n```lisp\n;(add-hook *prompt-after-activate-hook*\n;          (lambda ()\n;            (call-command 'lem/prompt-window::prompt-completion nil)))\n\n;(add-hook *prompt-deactivate-hook*\n;          (lambda ()\n;            (lem/completion-mode:completion-end)))\n\n```\n### Terminal commands\n(by cxxxr on Discord)\n\n(in-package :lem-terminal/terminal-mode)\n(defun send-string-and-newline (terminal string)\n  (loop :for character :across string\n        :do (terminal:input-character terminal\n                                      character))\n  (terminal:input-key terminal ffi::vterm_key_enter))\n\n(define-command terminal-send-command (string) ((:string \"String: \"))\n  (let ((buffer (terminal:find-terminal-buffer)))\n    (when buffer\n      (send-string-and-newline (buffer-terminal buffer) string))))\n## media-player (POC)\nI started playing with a media player: rely on mpv and its inter-process communication to load files and send commands to the player. It's easy, it works great.\n\nWe have commands from Lem to play files or directories: M-x media-player-play-directory RET select directory RET.\n\nOr, from a directory mode listing, M-x media-player-play or right-click -\u003e play file.\n\nI'm trying to add context menu entries to access player controls from anywhere.\nWe'll need a buffer with controls and hotkeys too.\n\nSee https://github.com/vindarel/lem-media-player and https://dev.to/vindarel/common-lisp-a-command-line-interactive-terminal-application-in-2-lines-2gnb\nWARN: hardcoded paths below.\n\n```lisp\n\n(format t \"---- loading media-player…~\u0026\")\n(load \"~/dotfiles/lem/media-player/media-player.lisp\")\n(load \"~/dotfiles/lem/media-player/lem-media-player.lisp\")\n\n```\n\n## Erudite: produce this README.md\n\nThe command required to produce the readme is:\n\n```\n(erudite:erudite #p\"README.md\" \"lem-init.lisp\" :output-type :markdown :syntax :erudite)\n```\n\nNB: I had to tweak cl-template's .asd definition to `:cl-template` instead of `#:cl-template`.\n\n## See also\n\n* https://gitlab.com/sasanidas/lem-config/-/blob/master/init.lisp\n* https://github.com/garlic0x1/.lem\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvindarel%2Flem-init","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvindarel%2Flem-init","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvindarel%2Flem-init/lists"}