平衡点


2026/02/04

_ Emacs で Modern Fortran を弄るためのアレコレ

いやぁ、結構頑張ったな(笑)

完成版

設定はこの辺

何がしたいか?

そもそもの発端はコードの「畳み込み(fold)」がやりたいというもの. 良くある「畳み込み」は

あたりかな.

でも, 作業したい言語は fortran ≧ 90 なので, あまり上手くない. インデントベースの folding はまあ動くのかもしれないけれど, ちょっと挙動が怪しいのと

module foo

  ! some code
contains
  function bar
    ! some code
  end function bar

  subroutine baz
    ! some code
  end subroutine baz
end module foo

というコードだと, 上手く fold できないみたい.

tree-sitter による言語解析の結果を利用した

というパッケージがあるので, じゃあこれを使えば良いのでは…と.

結果として大幅に advice を書くハメになった訳ですが(笑)

f90-ts-mode

tree-sitterによるsyntax hightlight の Modern Fortran 対応:

で, これを使えば良いのかな, と思ったけどイマイチ syntax highlight が対応していない, 様な? とはいえ, 言語解析自体は良いので, 自分好みに幾つか highlight を追加しておく.

(leaf f90-ts-mode
  :vc (:url "git@github.com:mscfd/emacs-f90-ts-mode.git")
  :mode ("\\.f90\\';" "\\.F90\\';" "\\.f95\\';" "\\.F95\\';" "\\.F\\';")
  :defvar treesit-language-source-alist
  :bind (:f90-ts-mode-map
         ("C-c ';" . separedit))
  :preface
;;;###autoload
  (defun my:install-fortran-grammar-with-generate ()
    "install 時に tree-sitter generateを実行してから install"
    (interactive)
    (let* ((lang ';fortran)
           (url "https://github.com/mscfd/tree-sitter-fortran")
           (revision "master")
           (tmp-dir (expand-file-name "tree-sitter-fortran-tmp" temporary-file-directory)))
      (when (file-exists-p tmp-dir)
        (delete-directory tmp-dir t))
      (message "Cloning %s..." url)
      (call-process "git" nil nil nil "clone" "-b" revision "--depth" "1" url tmp-dir)
      (message "Running tree-sitter generate...")
      (let ((default-directory tmp-dir))
        (call-process-shell-command "tree-sitter generate" nil nil nil))
      (let ((treesit-language-source-alist `((,lang ,tmp-dir nil "src"))))
        (treesit-install-language-grammar lang))
      (message "Fortran grammar installed successfully from generated source!")))
;;;###autoload
  (defun my:f90-ts-toggle-mode ()
    "Toggle between `f90-mode'; (legacy) and `f90-ts-mode';."
    (interactive)
    (if (eq major-mode ';f90-ts-mode)
        (if (fboundp ';f90-mode)
            (f90-mode)
          (message "Legacy `f90-mode'; not available"))
      (f90-ts-mode)))
;; function 関連の色付けルールがまだ熟れていないので, ちょっと修正する
(defvar my:f90-keywords
  ';("allocated" "associated" "present" "allocate" "deallocate" "nullify"))
(defvar my:f90-builtins
  ';("min" "max" "abs" "mod" "sqrt" "sum"
    "size" "shape" "len" "trim" "adjustl" "adjustr"
    "real" "int" "dble" "cmplx" "count" "any" "all"))
;;;###autoload
(defun my:f90-make-regex (word-list)
  (let ((patterns (mapcar (lambda (word)
                            (mapconcat (lambda (c)
                                         (format "[%c%c]" (upcase c) (downcase c)))
                                       word ""))
                          word-list)))
    (format "^\\(%s\\)$" (mapconcat #';identity patterns "\\|"))))
;;;###autoload
(defun my:fixed-f90-ts-rules-function ()
  "Fixed font-lock rules for functions and subroutines."
  (let ((regex-kw (my:f90-make-regex my:f90-keywords))
        (regex-bi (my:f90-make-regex my:f90-builtins)))
    (treesit-font-lock-rules
     :language ';fortran
     :feature ';function
     `((subroutine_statement
        name: (name)                  @font-lock-function-name-face)
       (function_statement
        name: (name)                  @font-lock-function-name-face)
       (function_result
        (identifier)                  @default)
       (subroutine_call
        subroutine: (identifier)      @font-lock-function-name-face)
       ;; --- 削除箇所 ---
       ;; (call_expression (identifier) @font-lock-function-name-face)
       ((call_expression (identifier) @font-lock-keyword-face)
        (:match ,regex-kw             @font-lock-keyword-face))
       ((call_expression (identifier) @font-lock-builtin-face)
        (:match ,regex-bi             @font-lock-builtin-face))
       ;; within derived type declarations
       (variable_declaration
        type: (procedure
               "procedure"
               (procedure_interface)        @font-lock-function-name-face)
        ;; is this always a pointer to a procedure?
        declarator: (identifier)            @font-lock-function-name-face)
       (procedure_statement
        declarator: [
                     ((method_name)         @font-lock-function-name-face)
                     ((binding
                       (binding_name
                        [
                         ((identifier)      @font-lock-function-name-face)
                         ((operator
                           (operator_name)  @f90-ts-font-lock-operator-face))
                         ((assignment "="   @f90-ts-font-lock-operator-face))
                         ])
                       (method_name)        @font-lock-function-name-face))
                     ])
       (generic_statement
        declarator: (binding_list
                     (binding_name
                      [
                       ((identifier)       @font-lock-function-name-face)
                       ((operator
                         (operator_name)   @f90-ts-font-lock-operator-face))
                       ((assignment "="    @f90-ts-font-lock-operator-face))
                       ])
                     (method_name)         @font-lock-function-name-face))
       (final_statement
        declarator: (method_name)           @font-lock-function-name-face)
       ))))
;;;###autoload
  (defun my:fixed-f90-ts-rules-variable ()
    "Fixed font-lock rules for variables, declarations, and module control."
    (treesit-font-lock-rules
     :language ';fortran
     :feature ';variable
     ';(
       ;; 1. 変数宣言 (integer :: nm, real :: x=1.0)
       (variable_declaration (init_declarator left: (identifier) @font-lock-variable-name-face))
       (variable_declaration (identifier) @font-lock-variable-name-face)
       (variable_declaration (sized_declarator (identifier) @font-lock-variable-name-face))
       ;; 2. SAVE
       (save_statement (identifier) @font-lock-variable-name-face)
       ;; 3. モジュール制御 (use, only, private, public)
       (use_statement (module_name) @font-lock-constant-face)
       (included_items (identifier) @font-lock-variable-name-face) ;; use ..., only: DP
       (public_statement (identifier) @font-lock-function-name-face)
       (private_statement (identifier) @font-lock-function-name-face)
       ;; 4. プログラム単位の名前
       (module_statement (name) @font-lock-function-name-face)
       (program_statement (name) @font-lock-function-name-face)
       ;; 5. 元のルール (self)
       ((identifier) @f90-ts-font-lock-special-var-face
        (:pred f90-ts-special-var-p @f90-ts-font-lock-special-var-face))
       )))
  ;;
  (add-to-list ';major-mode-remap-alist ';(f90-mode . f90-ts-mode))
  :advice
  ((:override f90-ts--font-lock-rules-function my:fixed-f90-ts-rules-function)
   (:override f90-ts--font-lock-rules-variable my:fixed-f90-ts-rules-variable)
   )
  :custom-face
  (f90-ts-font-lock-intrinsic-face       . ';((t :inherit font-lock-builtin-face)))
  (f90-ts-font-lock-delimiter-face       . ';((t :inherit font-lock-delimiter-face)))
  (f90-ts-font-lock-operator-face        . ';((t :inherit font-lock-operator-face)))
  (f90-ts-font-lock-openmp-face          . ';((t :inherit font-lock-preprocessor-face)))
  (f90-ts-font-lock-bracket-face         . ';((t :inherit font-lock-bracket-face)))
  (f90-ts-font-lock-special-var-face     . ';((t :inherit font-lock-constant-face)))
  (f90-ts-font-lock-special-comment-face . ';((t :inherit font-lock-doc-face)))
  :init
  (setq f90-ts-indent-block 2
        f90-ts-indent-contain 2
        f90-ts-indent-continued 2
        f90-ts-indent-toplevel 2
        f90-ts-indent-lists-region ';continued-line
        f90-ts-indent-lists-line   ';continued-line
        )
  (setq f90-ts-indent-rules-openmp
        `(
          ((f90-ts--openmp-comment-is) prev-sibling 0)
          ((f90-ts--openmp-comment-is) parent-bol f90-ts-indent-block)))
  ;; :config
  :hook
  (f90-ts-mode-hook
   . (lambda ()
       (setq-local comment-start "!")
       (setq-local comment-start-skip "\\(!>\\|!+\\)[ \t]*")
       (font-lock-add-keywords
        nil
        ';(("!>.*\\(\\*\\*\\(.*?\\)\\*\\*\\)" (1 ';font-lock-bold-face prepend))
          ("!>.*\\(`\\(.*?\\)`\\)" (1 ';font-lock-constant-face prepend))
          ("!>\\([ \t]*-[ \t]+\\)" (1 ';font-lock-keyword-face prepend))
          ("!>\\([ \t]*#+ .*\\)$" (1 ';font-lock-builtin-face prepend)))
        ';append)
       (setq-local treesit-font-lock-level 5)
       (treesit-font-lock-recompute-features)
       (treesit-fold-mode)
       )
   )
  )

とりあえず自分好みにはなったので, あとで PR でも投げる予定

treesit-fold

tree-sitter の結果をベースにしたコードの折り畳み.

…なんだけれど, あたり前の様に Modern Fortran に対応している訳もなく.

(leaf treesit-fold
  :if (executable-find "tree-sitter")
  :ensure t
  :bind (("C-c f" . treesit-fold-toggle)
         ("C-c F" . my:treesit-fold-toggle-all)
         )
  :preface
;;;###autoload
  (defun my:treesit-fold-toggle-all ()
    "Toggle between `treesit-fold-open-all'; and `treesit-fold-close-all';."
    (interactive)
    (let ((found-folded nil))
      (save-excursion
        (dolist (ov (overlays-in (point-min) (point-max)))
          (when (eq (overlay-get ov ';invisible) ';treesit-fold)
            (setq found-folded t))))
      (if found-folded
          (progn
            (treesit-fold-open-all)
            (message "All nodes opened"))
        (progn
          (treesit-fold-close-all)
          (message "All nodes closed")))))
;;;###autoload
  (defun my:treesit-fold-toggle-override (&rest _args)
    "Toggle fold at point. If opening, open recursively."
    (interactive)
    (if-let* ((node (treesit-fold--foldable-node-at-pos))
              (ov (treesit-fold-overlay-at node)))
        (treesit-fold-open-recursively)
      (treesit-fold-close)))
  :advice
  (:override treesit-fold-toggle my:treesit-fold-toggle-override)
  :hook ((treesit-fold-mode-hook . treesit-fold-line-comment-mode)
         (emacs-lisp-mode-hook . (lambda () (treesit-parser-create ';elisp)))
         )
  :config
  ;; ---------------------------------------------------------------------------
  ;; Fortran Support Implementation
  ;; ---------------------------------------------------------------------------
  ;; Constants of fold target
  (defconst treesit-fold-fortran-declaration-nodes
    ';("use_statement" "implicit_statement" "import_statement" "include_statement"
      "private_statement" "public_statement" "save_statement" "parameter_statement"
      "data_statement" "variable_declaration" "namelist_statement" "common_statement"
      "equivalence_statement" "entry_statement" "format_statement")
    "List of declaration node types in Fortran to be folded as a group.")
  ;; Check Sibling Node
  (defun treesit-fold-range-fortran--sibling (node next)
    "Find the nearest sibling of NODE.
If NEXT is non-nil, search forwards; otherwise, search backwards.
Skips comments, newlines, and end-of-statement markers."
    (let ((iter (if next (treesit-node-next-sibling node)
                  (treesit-node-prev-sibling node))))
      (while (and iter
                  (let ((type (treesit-node-type iter)))
                    (or (string-equal type "comment")
                        (string-equal type "\n")
                        (string-prefix-p "_end_of_" type)
                        (string-match-p "\\`\\s-*\\';" type))))
        (setq iter (if next (treesit-node-next-sibling iter)
                     (treesit-node-prev-sibling iter))))
      iter))
  ;; Range Parsers
  (defun treesit-fold-range-fortran-declarations (node offset)
    "Calculate the fold range for consecutive declaration blocks.
Merges adjacent declaration nodes into a single fold.
Also includes trailing comments (e.g. !$omp or normal comments) in the block."
    (let ((type (treesit-node-type node)))
      (when (member type treesit-fold-fortran-declaration-nodes)
        (let ((prev (treesit-fold-range-fortran--sibling node nil))
              (next (treesit-fold-range-fortran--sibling node t)))
          ;; Only fold from the first node in a sequence of declarations.
          (unless (and prev (member (treesit-node-type prev) treesit-fold-fortran-declaration-nodes))
            ;; 1. Determine the end of the strict declaration block
            (let ((last-node node)
                  (iter-node next))
              (while (and iter-node
                          (member (treesit-node-type iter-node) treesit-fold-fortran-declaration-nodes))
                (setq last-node iter-node)
                (setq iter-node (treesit-fold-range-fortran--sibling iter-node t)))
              ;; 2. Extend sibling range if it';s a comment or line break.
              (let ((scan-node (treesit-node-next-sibling last-node)))
                (while (and scan-node
                            (let ((t-type (treesit-node-type scan-node)))
                              (or (string-equal t-type "comment")
                                  (string-equal t-type "\n")
                                  (string-match-p "\\`\\s-*\\';" t-type))))
                  (when (string-equal (treesit-node-type scan-node) "comment")
                    (setq last-node scan-node))
                  (setq scan-node (treesit-node-next-sibling scan-node))))
              ;; 3. Create fold if range is valid
              (when (not (eq node last-node))
                (treesit-fold--cons-add (cons (treesit-node-start node)
                                              (treesit-node-end last-node))
                                        offset))))))))
  ;; Comment block
  (defun treesit-fold-range-fortran-comment (node offset)
    "Calculate the fold range for consecutive comment blocks.
Skips empty lines and non-comment whitespace between comments."
    (when (string-equal (treesit-node-type node) "comment")
      (let ((prev (treesit-node-prev-sibling node))
            (is-continuation nil))
        ;; Check if this node is a continuation of a previous comment block.
        (while (and prev
                    (or (string-equal (treesit-node-type prev) "comment")
                        (string-match-p "\\`\\s-*\\';" (treesit-node-type prev))))
          (when (string-equal (treesit-node-type prev) "comment")
            (setq is-continuation t))
          (setq prev (treesit-node-prev-sibling prev)))
        ;; Only fold if this is the start of the block.
        (unless is-continuation
          (let ((next (treesit-node-next-sibling node))
                (last-comment node))
            ;; Find the end of the comment block.
            (while next
              (let ((type (treesit-node-type next)))
                (cond
                 ((string-equal type "comment")
                  (setq last-comment next)
                  (setq next (treesit-node-next-sibling next)))
                 ((string-match-p "\\`\\s-*\\';" type)
                  (setq next (treesit-node-next-sibling next)))
                 (t
                  (setq next nil)))))
            (when (not (eq last-comment node))
              (treesit-fold--cons-add (cons (treesit-node-start node)
                                            (treesit-node-end last-comment))
                                      offset)))))))
  ;; Normal fold blocks
  (defun treesit-fold-range-fortran-blocks (node offset)
    "Calculate the fold range for generic blocks (e.g., subroutines, loops).
Folds the content between the header (first child) and the footer (last child)."
    (let ((header (treesit-node-child node 0 t))
          (footer (treesit-node-child node -1 t)))
      (when (and header footer (not (eq header footer)))
        (treesit-fold--cons-add (cons (treesit-node-end header)
                                      (treesit-node-start footer))
                                offset))))
  ;; Summary Parser
  (defun treesit-fold-summary-fortran-doc (doc-str)
    "Extract summary from Fortran comments.
Handles Doxygen style (!>) and ignores code blocks or separator lines."
    (when (string-match-p "\\`\\s-*!" doc-str)
      ;; Ensure the content is strictly a comment block (no code lines).
      (unless (string-match-p "\n[ \t]*[^! \t\n]" doc-str)
        (let ((lines (split-string doc-str "\n"))
              (summary nil))
          (dolist (line lines)
            (unless summary
              ;; Remove ';!';, ';!>';, or whitespace.
              (let ((content (replace-regexp-in-string "\\`\\s-*!+>?\\s-*" "" line)))
                (cond
                 ((string-empty-p content) nil)
                 ((string-match-p "-[*]-" line) nil) ; Ignore mode lines
                 ((not (string-match-p "[[:alnum:]]" content)) nil) ; Ignore separators
                 (t (setq summary content))))))
          summary))))
  ;; Definition of Rules
  (defun treesit-fold-parsers-fortran ()
    "Return the folding rules for f90-ts-mode."
    `((subroutine                 . treesit-fold-range-fortran-blocks)
      (function                   . treesit-fold-range-fortran-blocks)
      (interface                  . treesit-fold-range-fortran-blocks)
      (derived_type_definition    . treesit-fold-range-fortran-blocks)
      (do_loop                    . treesit-fold-range-fortran-blocks)
      (if_statement               . treesit-fold-range-fortran-blocks)
      (select_case_statement      . treesit-fold-range-fortran-blocks)
      (select_type_statement      . treesit-fold-range-fortran-blocks)
      (block_construct            . treesit-fold-range-fortran-blocks)
      (associate_statement        . treesit-fold-range-fortran-blocks)
      (coarray_critical_statement . treesit-fold-range-fortran-blocks)
      (forall_statement           . treesit-fold-range-fortran-blocks)
      (where_statement            . treesit-fold-range-fortran-blocks)
      (module                     . treesit-fold-range-fortran-blocks)
      (program                    . treesit-fold-range-fortran-blocks)
      (submodule                  . treesit-fold-range-fortran-blocks)
      ,@(mapcar (lambda (type) (cons (intern type) ';treesit-fold-range-fortran-declarations))
                treesit-fold-fortran-declaration-nodes)
      (comment                    . treesit-fold-range-fortran-comment)))
  ;; Register summary parser for f90-ts-mode.
  (add-to-list ';treesit-fold-summary-parsers-alist
               ';(f90-ts-mode . treesit-fold-summary-fortran-doc))
  ;; 以下は私の好み.
  ;; module や program が fold されると全部 fold されるので構造がわかりにくい.
  (add-to-list ';treesit-fold-range-alist
               (cons ';f90-ts-mode
                     (seq-remove (lambda (x)
                                   (memq (car x) ';(module program submodule)))
                                 (treesit-fold-parsers-fortran))))
  )

これで

  • C-c f で部分の fold のトグル
  • C-c F で全体の fold のトグル

ができた!

separedit

最後に, コード中にドキュメントを書くためにじたばた.

Fortran のコード中に Doxygen Markdown でコメントを書きたいのだけれど (この場合, 行頭は !> で始まる), そのまま Markdown を書くのは結構しんどい.

どうせ皆, ドキュメントなんて書いてない みな, どうしてるんだろうね.

そんな訳で, org-mode のコードブロックの様に編集可能な separedit を Modern Fortran の Doxygem Markdown に対応させてみた.

(leaf separedit
  :ensure t
  :init
  (setq separedit-default-mode ';markdown-mode
        separedit-remove-trailing-spaces-in-comment t
        separedit-preserve-string-indentation t
        separedit-continue-fill-column t)
  :defer-config
  (push ';(("!>" "!") . f90-ts-mode) separedit-comment-delimiter-alist)
  :advice (:around separedit my:speparedit-doxygen-fortran)
  :preface
;;;###autoload
(defun my:speparedit-doxygen-fortran (orig-fun &rest args)
  "Separate Edit for Doxygen Markdown (!>) and normal comment (!)"
  (if (derived-mode-p ';f90-ts-mode)
      (cond
       ;; -------------------------------------------------------------------
       ;; case A: Doxygen Markdown (!>) 
       ;; -------------------------------------------------------------------
       ((save-excursion (beginning-of-line) (looking-at-p "^[ \t]*!>"))
        (let ((beg (save-excursion
                     (beginning-of-line)
                     (while (and (not (bobp))
                                 (save-excursion
                                   (forward-line -1)
                                   (beginning-of-line)
                                   (looking-at-p "^[ \t]*!>")))
                       (forward-line -1))
                     (point)))
              (end (save-excursion
                     (end-of-line)
                     (while (and (not (eobp))
                                 (save-excursion
                                   (forward-line 1)
                                   (beginning-of-line)
                                   (looking-at-p "^[ \t]*!>")))
                       (forward-line 1))
                     (end-of-line)
                     (point))))
          (funcall orig-fun (list :beginning beg
                                  :end end
                                  :lang-mode ';markdown-mode
                                  :comment-delimiter "^[ \t]*!>[ \t]*"))))
       ;; -------------------------------------------------------------------
       ;; case B: nmormal comment(!) but not Doxygen Fortran (!> )
       ;; -------------------------------------------------------------------
       ((save-excursion (beginning-of-line) (looking-at-p "^[ \t]*![^>]"))
        (let ((beg (save-excursion
                     (beginning-of-line)
                     ;; Move up, stop "!>"
                     (while (and (not (bobp))
                                 (save-excursion
                                   (forward-line -1)
                                   (beginning-of-line)
                                   (and (looking-at-p "^[ \t]*!")
                                        (not (looking-at-p "^[ \t]*!>")))))
                       (forward-line -1))
                     (point)))
              (end (save-excursion
                     (end-of-line)
                     ;; Move down, stop "!>"
                     (while (and (not (eobp))
                                 (save-excursion
                                   (forward-line 1)
                                   (beginning-of-line)
                                   (and (looking-at-p "^[ \t]*!")
                                        (not (looking-at-p "^[ \t]*!>")))))
                       (forward-line 1))
                     (end-of-line)
                     (point))))
          (funcall orig-fun (list :beginning beg
                                  :end end
                                  :lang-mode ';markdown-mode
                                  :comment-delimiter "^[ \t]*![ \t]*"))))
       ;; -------------------------------------------------------------------
       ;; default: other case
       ;; -------------------------------------------------------------------
       (t (apply orig-fun args)))
    ;; not f90-ts-mode
    (apply orig-fun args)))
)

まとめ.

快適になった.

いや, さっさとコード書けよ, というのは, まあそうなんだけれど


2026/02/14

_ Windows11, macOS, Linux (Wayland) での clipboard 共有

始めに

キーボードとマウスの共有は deskflow/deskflow: Share a single keyboard and mouse between multiple computers. でできているのだけれど, Wayland を動かしている Linux と他の OS との Clipbard 共有がまだ上手くできていない.

このためだけに KDEConnect を入れてクリップボード共有を行なっていたのだけど, どうも GNOME の gsconnect がエラーを吐いて共有ができなくなってしまった.

…手軽で良かったんだけれどな.

そんな訳で, Sathvik-Rao/ClipCascade を試してみることに.

導入

サーバは

% java -jar ClipCascade-Server-JRE_21.jar

で良いみたい. もしくは docker で

% docker run -d --name clipcascade -p 8080:8080 -e CC_MAX_MESSAGE_SIZE_IN_MiB=1 -v ./cc_users:/database sathvikrao/clipcascade

みたいな?

というわけで, podman を利用して

[Unit]
Description=Docker ClipCascade
Documentation=https://github.com/Sathvik-Rao/ClipCascade
After=graphical-session.target default.target

[Service]
Type=simple
Environment=CC_MAX_MESSAGE_SIZE_IN_MiB=10
ExecStartPre=-/usr/bin/mkdir -p %h/.local/share/ClipCascade
ExecStartPre=-/usr/bin/podman stop ClipCascade
ExecStartPre=-/usr/bin/podman rm ClipCascade
ExecStartPre=-/usr/bin/podman pull docker.io/sathvikrao/clipcascade:0.7.0
ExecStart=/usr/bin/podman run --name ClipCascade -p 192.168.253.13:18080:8080  -v %h/.local/share/ClipCascade:/database docker.io/sathvikrao/clipcascade:0.7.0

[Install]
WantedBy=multi-user.target

みたいな感じで.

クライアントはまあ, 書かれている通りかな.

  • Windows は入れて起動するだけでOK
  • macOS はセキュリティ関連が面倒, かな.
  • Linux は python3 実装なのでシュッっと venv 唱えて動かすと OK.

結論

とりあえず Clipboard 共有が戻ってきた. しばらく使ってみる.

KDEConnectとgsconnectの方が楽なんだよなぁ…


2026/02/16

_ corfu の設定を見直す (2026年版)

以前は

丸パクリ 参考に設定していのだけれど, 最近これが動いていない事に気がついた.

でもって, 改めて corfu の設定を最初からやり直してみたのだけれど

  • そもそも手癖で tab を打ってしまい, 望まぬ最初の補完候補が展開されるのが嫌
  • それはそれとして, 補完候補がある場合は候補は表示して欲しい

となっていれば良いわけで.

  1. corf は最初から候補を選択している訳ではないので, TAB に割り振られた corf-insert が実行されても, 候補を展開したりはしない.
  2. ここで tab-always-indent が ='complete= だと再度補完が発生して…
  3. なので tab-always-indentt なら, 望まぬ補完候補の展開は発生しない

となっていた. なるほど.

というわけで, 結局諸々の設定を綺麗にして, 以下の様に. corf-terminalkind-iconnerd-icon の設定が長いが, まあそれはそれとして.

(leaf corfu
  :ensure t
  :init
  ;; -------------------------------------------------------------------------
  (leaf corfu-terminal
    :ensure t
    :config
    (setq corfu-terminal-disable-on-gui nil
          corfu-terminal-enable-on-minibuffer nil
          corfu-terminal-resize-minibuffer nil
          )
    )
  ;; -------------------------------------------------------------------------
  (leaf kind-icon
    :ensure t
    :after corfu-terminal
    :init
    (setq kind-icon-default-face 'corfu-default
          kind-icon-use-icons    nil
          kind-icon-mapping
          `(
            (array          ,(nerd-icons-codicon "nf-cod-symbol_array")       :face font-lock-type-face)
            (boolean        ,(nerd-icons-codicon "nf-cod-symbol_boolean")     :face font-lock-builtin-face)
            (class          ,(nerd-icons-codicon "nf-cod-symbol_class")       :face font-lock-type-face)
            (color          ,(nerd-icons-codicon "nf-cod-symbol_color")       :face success)
            (command        ,(nerd-icons-codicon "nf-cod-terminal")           :face default)
            (constant       ,(nerd-icons-codicon "nf-cod-symbol_constant")    :face font-lock-constant-face)
            (constructor    ,(nerd-icons-codicon "nf-cod-triangle_right")     :face font-lock-function-name-face)
            (enummember     ,(nerd-icons-codicon "nf-cod-symbol_enum_member") :face font-lock-builtin-face)
            (enum-member    ,(nerd-icons-codicon "nf-cod-symbol_enum_member") :face font-lock-builtin-face)
            (enum           ,(nerd-icons-codicon "nf-cod-symbol_enum")        :face font-lock-builtin-face)
            (event          ,(nerd-icons-codicon "nf-cod-symbol_event")       :face font-lock-warning-face)
            (field          ,(nerd-icons-codicon "nf-cod-symbol_field")       :face font-lock-variable-name-face)
            (file           ,(nerd-icons-codicon "nf-cod-symbol_file")        :face font-lock-string-face)
            (folder         ,(nerd-icons-codicon "nf-cod-folder")             :face font-lock-doc-face)
            (interface      ,(nerd-icons-codicon "nf-cod-symbol_interface")   :face font-lock-type-face)
            (keyword        ,(nerd-icons-codicon "nf-cod-symbol_keyword")     :face font-lock-keyword-face)
            (macro          ,(nerd-icons-codicon "nf-cod-symbol_misc")        :face font-lock-keyword-face)
            (magic          ,(nerd-icons-codicon "nf-cod-wand")               :face font-lock-builtin-face)
            (method         ,(nerd-icons-codicon "nf-cod-symbol_method")      :face font-lock-function-name-face)
            (function       ,(nerd-icons-codicon "nf-cod-symbol_method")      :face font-lock-function-name-face)
            (module         ,(nerd-icons-codicon "nf-cod-file_submodule")     :face font-lock-preprocessor-face)
            (numeric        ,(nerd-icons-codicon "nf-cod-symbol_numeric")     :face font-lock-builtin-face)
            (operator       ,(nerd-icons-codicon "nf-cod-symbol_operator")    :face font-lock-comment-delimiter-face)
            (param          ,(nerd-icons-codicon "nf-cod-symbol_parameter")   :face default)
            (property       ,(nerd-icons-codicon "nf-cod-symbol_property")    :face font-lock-variable-name-face)
            (reference      ,(nerd-icons-codicon "nf-cod-references")         :face font-lock-variable-name-face)
            (snippet        ,(nerd-icons-codicon "nf-cod-symbol_snippet")     :face font-lock-string-face)
            (string         ,(nerd-icons-codicon "nf-cod-symbol_string")      :face font-lock-string-face)
            (struct         ,(nerd-icons-codicon "nf-cod-symbol_structure")   :face font-lock-variable-name-face)
            (text           ,(nerd-icons-codicon "nf-cod-text_size")          :face font-lock-doc-face)
            (typeparameter  ,(nerd-icons-codicon "nf-cod-list_unordered")     :face font-lock-type-face)
            (type-parameter ,(nerd-icons-codicon "nf-cod-list_unordered")     :face font-lock-type-face)
            (unit           ,(nerd-icons-codicon "nf-cod-symbol_ruler")       :face font-lock-constant-face)
            (value          ,(nerd-icons-codicon "nf-cod-symbol_field")       :face font-lock-builtin-face)
            (variable       ,(nerd-icons-codicon "nf-cod-symbol_variable")    :face font-lock-variable-name-face)
            (t              ,(nerd-icons-codicon "nf-cod-code")               :face font-lock-warning-face))
          )
    :config
    (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter)
    )
  ;; -------------------------------------------------------------------------
  :bind (:corfu-map
         ("M-SPC" . corfu-insert-separator))
  ;; -------------------------------------------------------------------------
  :hook (emacs-startup-hook . (lambda ()
                                (corfu-terminal-mode +1)
                                (global-corfu-mode))
                            )
  ;; -------------------------------------------------------------------------
  :custom (;; Emacs 30 and newer: Disable Ispell completion function.
           ;; Try `cape-dict' as an alternative.
           (text-mode-ispell-word-completion . nil)
           )
  ;; -------------------------------------------------------------------------
  :config
  (setq corfu-cycle                t
        corfu-auto                 t
        tab-always-indent          t  ;; 'complete だと TAB で補完暴発が...
        completion-cycle-threshold 4
        corfu-preselect            'prompt
        )
  )

解決したかった TAB の暴走は無くなったけれど, 今度は corf-separator の割り当てに悩んだり.

よくある SPC だと, 英字の後に半角空白を入れて空ける癖と衝突したりして. 取り敢えず M-SPC にしたけれど, どうかなぁ….


2026/02/18

_ AUCTeX と latexmk の連携(2026年版)

昔の話は以下

最近は typeset のコマンド調整などは latexmk に処理を任せてしまい, AUCTeXは編集補助にしか使っていなかったのだけれど, Emacs から latexmk を走らせても良いよね, という気分になったので.

AUCTeX の本体の方では以下のコミットで LaTeXMk のサポートが入った模様

しかしながら, Debian パッケージ版はちょっと古いので, じたばたと.

最終的には, 以下の様な感じで.

(leaf auctex
  :if (and (executable-find "uplatex")
           (executable-find "latexmk"))
  :preface
  ;; -------------------------------------------------------------------------
  ;; Debian パッケージを利用するための調整
  (add-to-list 'my:package-menu-exclude-packages "auctex")
  (if (and (not (featurep 'text-site))
           (file-directory-p "/usr/share/auctex"))
      (progn
        (add-to-list 'load-path "/usr/share/auctex")
        (add-to-list 'load-path "/usr/share/emacs/site-lisp/auctex")))
  ;; -------------------------------------------------------------------------
  (unless (file-directory-p (expand-file-name "auctex/auto" my:d:tmp))
    (make-directory (expand-file-name "auctex/auto" my:d:tmp) t)
    (make-directory (expand-file-name "auctex/style" my:d:tmp) t))
  ;; -------------------------------------------------------------------------
  :mode  ("\\.tex\\'" . japanese-latex-mode)
  ;; -------------------------------------------------------------------------
  :hook
  ((emacs-startup-hook . (lambda ()
                           (load "auctex.el" nil t)
                           ;; (load "preview-latex.el" nil t)
                           ))
   (LaTeX-mode-hook    . (lambda ()
                           (add-to-list 'TeX-expand-list
                                        '("%(-PDF)"
                                          (lambda ()
                                            (if TeX-PDF-mode
                                                (cond
                                                 ((eq TeX-engine 'default) "-pdf")
                                                 ((eq TeX-engine 'uptex)   "-pdfdvi")
                                                 ((eq TeX-engine 'ptex)    "-pdfdvi")
                                                 ((eq TeX-engine 'xetex)   "-pdfxe")
                                                 ((eq TeX-engine 'luatex)  "-pdflua")) ""))))
                           (add-to-list 'TeX-command-list
                                        '("LaTeXmk" "latexmk %(-PDF) -pvc  %(output-dir) %t"
                                          TeX-run-format nil (latex-mode doctex-mode) :help "Run Latexmk"))
                           (setq TeX-command-output-list
                                 '(("LaTeXmk" ("pdf"))))
                           (setq TeX-command-default "LaTeXmk")
                           (eglot-ensure)
                           (turn-on-reftex)
                           (TeX-source-correlate-mode)
                           (TeX-PDF-mode)
                           (LaTeX-math-mode)
                           ))
   )
  :init
  ;; -------------------------------------------------------------------------
  ;; 基本設定
  (setq TeX-auto-local (expand-file-name "auctex/auto" my:d:tmp)
        TeX-style-local (expand-file-name "auctex/style" my:d:tmp)
        TeX-auto-untabify t
        TeX-engine 'uptex
        TeX-source-correlate-method 'synctex
        TeX-source-correlate-start-server t
        TeX-source-correlate-mode t
        TeX-ispell-extend-skip-list t
        TeX-electric-sub-and-superscript t
        ;; -------------------------------------------------------------------
        LaTeX-math-mode t
        LaTeX-figure-label "fig:"
        LaTeX-table-label "tab:"
        LaTeX-section-label "sec:"
        ;; -------------------------------------------------------------------
        reftex-plug-into-AUCTeX t
        reftex-cite-prompt-optional-args t
        ;; -------------------------------------------------------------------
        font-latex-fontify-script nil
        font-latex-script-display nil
        font-latex-fontify-sectioning  1.0
        ;; -------------------------------------------------------------------
        ;; TeX-auto-save t
        ;; TeX-parse-self t
        ;; TeX-master nil
        )
  )

ちなみにこれだと Emacs の裏でずっと latexmk が走り続けるので, どこかのタイミングでプロセスを止めなきゃだめなんだが, どうすっかなぁ.

あと TeX-command-listTeX-expand-listdefcustom な変数のハズなのに, tex ファイルを開くと, 設定が全て無視されて初期値に戻る. これはいったいどういう処理なんだろうか…


2026/02/19

_ WinApps を docker 上の Windows から libvirt に変えた

WinApps は VM 上のアプリケーションを Xfreerdp でウィンドウだけ切り取って起動して, まるで native な環境で作業している気分になれる素敵なソフトウェア.

ちょっと前まで docker 上で動かしていたんだけれど, docker の volume とか整理してたら winapps の volume も消してしまった. エクセルとワードから逃れられない身としては, これは気軽に docker 上でアレコレするのはマズいねぇ, ということで, libvirt に移行.

といっても特にメモしておく事は無くて,

この文書自体が winapps よりも libvirt 上に Windows11 環境を導入する とてもマトモな文書になっている, 様な.

なお WinApps のインストールには CJKV 特有の躓きポイントがあって

今時 cp932 が残っているのが悪い, と言う話かなぁ. 上記チケットにある workaround で解決しました.

それはそれとして, エクセルが CSV の数値を文字列にして読み込むおせっかいってドコで切るんだったっけ…?


2026/02/21

_ act を podman で動かす

Github Actions の検証に GitHub - nektos/act: Run your GitHub Actions locally 🚀 を使っていたのですが, とある計算機環境には podman しか入れてないので, はてどうするのかな, と.

単に socket を渡せば良かった.

% systemctl --user enable --now podman.socket
% export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
% act

というわけで.

誤字訂正

追記: Podman のネットワーク設定

特に何もしていないと, /etc/hosts/etc/resolv.conf がコンテナにコピーされるわけだけれど, laptop で動かしているので, 上流のDNSが suspend →resume でコロコロ変わる.

これは何とかならんかな, と.

  1. ~/.config/containers/containers.conf
    [containers]
    devices = ["/dev/kvm:/dev/kvm:rwm"]
    dns_servers = ['10.0.2.254']
    base_hosts_file = "/home/uwabami/.config/containers/hosts"
    host_containers_internal_ip="10.0.2.254"
    annotations = [
        "run.oci.keep_original_groups=1",
    ]
    
    [network]
    network_backend = "netavark"
    default_network="podman"
    default_rootless_network_cmd="pasta"
    pasta_options = ["-4", "-a", "10.0.2.1", "-n", "24", "-g", "10.0.2.254", "--dns-forward", "10.0.2.254"]
    
    [engine]
    events_logger="journald"
    
  2. ~/.config/containers/hosts まあ, 適当に hosts を書いておく.

これで, コンテナの DNS がアレコレ変わることもない, ということで.


連絡先など
"
最近の日記
  • 2026/02/21
    • 1. act を podman で動かす
  • 2026/02/19
    • 1. WinApps を docker 上の Windows から libvirt に変えた
  • 2026/02/18
    • 1. AUCTeX と latexmk の連携(2026年版)
  • 2026/02/16
    • 1. corfu の設定を見直す (2026年版)
  • 2026/02/14
    • 1. Windows11, macOS, Linux (Wayland) での clipboard 共有
一覧
2006|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|02|03|04|08|09|10|11|12|
2013|01|02|03|04|05|06|08|09|10|11|12|
2014|01|02|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|09|10|
2016|02|03|
2017|01|02|03|05|06|07|09|11|12|
2018|03|06|07|10|11|12|
2019|01|02|03|04|05|07|10|12|
2020|01|02|03|04|05|08|09|10|11|12|
2021|01|02|03|05|06|07|08|09|11|12|
2022|01|02|03|04|05|06|08|10|11|12|
2023|02|03|04|06|08|09|11|12|
2024|01|02|03|04|05|06|10|12|
2025|02|03|04|05|06|08|09|10|12|
2026|01|02|
Back to Top ▲