日記

2026-02-09

お久しぶりです。
最近仕事が忙しすぎて、タロットカードも書けないしブログも更新できていませんでした。
書くことがなかったということもありますが。

先週くらいからNeovim → Emacsに移行しました。
完全に移行できたかというと日が浅いのでまだわからないですが、既にEmacsのほうが快適な環境になりつつあるような気がします。

移行に至った経緯: 最も大きな理由は「そろそろEmacsに戻ってもいいかもな〜」という気分的なものですが、Neovimで使用しているtreesitterのプラグインが破壊的変更を行なったというのもあります。
詳細は別の日記を見るとわかると思います。

既にinit.elは1401行になり、かなりの設定が追加されました。
弄っていて楽しいのがいいところですね。Neovimはそこまで設定を頑張ろうという気持ちにならないのですが。
luaを書くのがそこまでなせいかもしれません。lispは楽しいので。
まだrubyだったら書く気になってたかもしれない。

今のinit.elをとりあえず載せます。
Emacs備忘録(2026)を参考にしつつ色々追加したものになります。

ちょっと長すぎるのでページの最後にしますね。

このサイトはJekyllで作られているのですが、それを編集するためのプラグインも入れてあります。
ただ、previewはうまくいかないですね。

;;; package --- 初期設定ファイル -*- lexical-binding: t; -*-
;;; Commentary:
;;; Emacsの全体的な設定やパッケージ管理を行います。
;;; Code:

;;; プロファイル
;; (require 'profiler)
;; (profiler-start 'cpu)

;;; Magick File Nameを一時的に無効化(最適化)
(defconst my/saved-file-name-handler-alist file-name-handler-alist)
(setq file-name-handler-alist nil)

;;; ユーザ定義関数の場所
(add-to-list 'load-path "~/.emacs.d/mylisp")

;;; --- Emacs基本設定 ---

;;; 煩わしいものを直す系
;;; ロックファイル・バックアップファイルを作成しない
(setopt make-backup-files nil
        backup-inhibited nil
        create-lockfiles nil)

(setq ring-bell-function 'ignore ; ビープ音を無効化
      use-short-answers t)       ; yes-or-noをy-or-nに変更

(setopt inhibit-startup-screen t                          ; 起動時のスタートメニュー非表示
        native-comp-async-report-warnings-errors 'silent) ; native-compの警告を表示しない


;;; 振舞い系
;; デフォルトの文字コードをutf-8にする
(prefer-coding-system 'utf-8)

;; 削除したファイルをゴミ箱に移動させる
(setopt delete-by-moving-to-trash t)

;; killできないようにするバッファーを一括指定できるマクロ
;; わざわざマクロ書く必要はないが、後学のために
(defmacro unkillable-buffers (&rest args)
  (let ((expr nil))
    (while args
      (push `(with-current-buffer ,(pop args) (emacs-lock-mode 'kill)) expr))
    `(progn ,@(nreverse expr))))

;; killできないようにするバッファー
(unkillable-buffers "*scratch*" "*Messages*")

;;; 処理速度系
;; ガベージコレクションの閾値を調整
(setq gc-cons-percentage 0.2
      gc-cons-threshold (* 128 1024 1024)
      garbage-collection-messages nil)

;; 外部プロセスの読み取り最大値を変更
(setq read-process-output-max (* 1024 1024))


;;; 編集系
;; タブ幅を4にし、タブをスペースとして入力するようにする
(setq-default tab-width 4
              indent-tabs-mode nil)

;; キルリングに新しいテキストを追加する前にクリップボードの内容を保存
(setopt save-interprogram-paste-before-kill t)

;; auto-fill-modeの時の文字数を指定
(setq fill-column 80)


;;; 視覚化系
;; 行数表示
(global-display-line-numbers-mode)

;; 対応する括弧を強調
(show-paren-mode t)

;; 現在行をハイライト
(global-hl-line-mode t)
(setq hl-line-face '((((class color) (background dark)) (:background "#191f3c" t))))

;;スペースやタブを可視化
(setq-default show-trailing-whitespace t)

;; font
(set-frame-font "HackGen Console NF-12")

;;; エラー対策
;; Error in post-command-hook (ccc-update-buffer-local-frame-params):
;; (void-function facement-color-equal) 対策
(require 'facemenu)

;;; OS判定
(defconst IS-MAC     (eq system-type   'darwin))
(defconst IS-LINUX   (memq system-type '(gnu gnu/linux gnu/kfreebsd berkeley-unix)))
(defconst IS-WINDOWS (memq system-type '(cygwin windows-nt ms-dos)))

(when IS-WINDOWS
  (setq w32-get-true-file-attributes nil
        w32-pipe-read-delay 0
        w32-pipe-buffer-size (* 64 1024)))

;; 文字コード
(when IS-WINDOWS
  (set-language-environment "UTF-8")
  (setopt file-name-coding-system 'cp932)
  (setopt default-process-conding-system '(utf-8-dos . japanese-cp932-dos)))

(setopt package-gnupghome-dir "~/AppData/Roaming/.emacs.d/elpa/gnupg") ; 認証情報の場所

;; package.el
(require 'package)

(add-to-list 'package-archives '("gnu-elpa-devel" . "https://elpa.gnu.org/devel/"))
(add-to-list 'package-archives '("nongnu-devel"   . "https://elpa.nongnu.org/nongnu-devel/"))
(add-to-list 'package-archives '("melpa"          . "https://melpa.org/packages/"))

;; インストール優先度
(setopt package-archive-priorities
        '(("gnu-elpa-devel" . 3)
          ("nongnu-devel"   . 2)
          ("melpa"          . 1)))

(setopt package-install-upgrade-built-in t ; built-inパッケージも更新対象にする
        package-native-compile t)          ; パッケージをコンパイルする

(use-package use-package
  :init
  (setopt
   use-package-enable-imenu-support t ; use-packageの設定をimenuで利用可能にする
   use-package-always-ensure t))       ; ensure tをすべてのuse-package宣言に適用

;; elpaディレクトリ上にtarball形式のパッケージを管理する用のリポジトリを作成し、
;; 自動的に管理するためのパッケージ。インストールや削除、アップグレードの操作時に
;; 自動的に状態をコミットしてバージョン管理する。
(use-package package-git
  :vc ( :url "https://github.com/kn66/package-git.el"
        :rev :newest)
  :functions package-git-enable
  :config
  (package-git-enable))

(use-package package-retry
  :vc ( :url "https://github.com/kn66/package-retry.el.git"
        :rev :newest)
  :functions package-retry-mode
  :config
  (package-retry-mode))

;;; 見た目系
;; テーマ設定
(use-package modus-themes
  :functions modus-themes-select
  :config
  (modus-themes-select 'modus-vivendi-tinted))

;; 時刻表示
(use-package time
  :init
  (setopt
   display-time-24hr-format t ; 24時間表記
   display-time-day-and-date t))

;; デーモン起動
(use-package server
  :config
  (server-mode))

;;; ファイル/ディレクトリ
;; dired
(use-package dired
  :functions
  dired-copy-filename-as-kill
  :ensure nil
  :init
  (setopt
   dired-dwim-target t            ; diredのtargetを別windowに
   dired-listing-switches "-alh") ; human-readable file size
  :config
  (defun dired-copy-fullpass-as-kill ()
    (interactive)
    (dired-copy-filename-as-kill 0)))

;;; 操作系
;; evil-mode
(use-package evil
  :functions evil-mode
  :init
  (setopt
   evil-want-C-u-scroll t       ; C-uでスクロール
   evil-undo-system 'undo-redo) ; undo-systemをundo-treeに
  :config
  (evil-mode t))

;; evil-numbers
(use-package evil-numbers
  :config
  (setopt evil-numbers-pad-default t)) ; インクリメント/デクリメントのために

;;; キーバインド
(use-package general
  :functions
  general-create-definer
  general-def
  general-auto-unbind-keys
  :config

  ;; prefixキーをスペースにする
  (general-create-definer my-leader-def
    :prefix "SPC")

  (general-auto-unbind-keys) ; prefixキーがbindされているものを自動的に解除

  (my-leader-def
    :keymaps '(normal visual)
    "ac" 'org-capture                       ; captureでメモ
    "ar" 'align-regexp                      ; 正規表現を使用した整形
    "at" 'org-agenda                        ; org-agenda表示
    "b"  'consult-buffer                    ; バッファ切り替え
    "B"  'list-buffers                      ; バッファリスト
    "cb" 'consult-buffer                    ; バッファ切り替え
    "cB" 'consult-bookmark                  ; ブックマーク
    "cd" 'consult-dir                       ; ディレクトリ検索
    "cf" 'consult-fd                        ; fd検索
    "cF" 'consult-flymake                   ; エラー検索
    "cg" 'consult-ripgrep                   ; ripgrep
    "cl" 'consult-line                      ; バッファ内検索
    "cr" 'consult-recent-file               ; 直近編集したファイル
    "cR" 'consult-ripgrep-current-directory ; カレントディレクトリでripgrep
    "d"  'dired                             ; diredを開く
    "es" 'eshell                            ; eshellを開く
    "ev" 'eval-last-sexp                    ; lispをeval
    "ff" 'find-file                         ; ファイルを探す
    "fn" 'flymake-goto-next-error           ; 次のエラー
    "fp" 'flymake-goto-prev-error           ; 前のエラー
    "gt" 'evil-tab-next                     ; 次のタブ
    "gT" 'tab-bar-switch-to-prev-tab        ; 前のタブ
    "hb" 'describe-bindings                 ; キーバインド一覧
    "hf" 'describe-function                 ; 関数のヘルプ
    "hh" 'help-for-help                     ; ヘルプのヘルプ
    "hk" 'describe-key                      ; キーバインドのヘルプ
    "hm" 'describe-mode                     ; modeのヘルプ
    "hv" 'describe-variable                 ; 変数のヘルプ
    "j"  'easy-jekyll
    "k"  'kill-buffer                       ; バッファを閉じる
    "lr" 'lsp-ui-peek-find-references       ; 参照を探す
    "ld" 'lsp-ui-peek-find-definitions      ; 定義を探す
    "li" 'lsp-ui-peek-find-implementation   ; 実装を探す
    "mg" 'magit                             ; magit起動
    "C-a" 'evil-numbers/inc-at-pt           ; インクリメント
    "C-x" 'evil-numbers/dec-at-pt           ; デクリメント
    "wo" 'delete-other-windows              ; current windowだけ残す
    "wc" 'delete-window                     ; current windowを消す
    "q"  'save-buffers-kill-terminal        ; emacs終了
    "rf" 'org-roam-node-find                ; org-roam内でfind file
    "rg" 'org-roam-graph                    ; グラフを作成して表示
    "sp" 'split-window-below                ; :sp
    "tc" 'tab-close                         ; tabclose
    "tn" 'tab-new                           ; tabnew
    "tr" 'gt-translate                      ; 翻訳
    "tw" 'toggle-truncate-lines             ; 折り返しのトグル
    "u"  'vundo                             ; undo-tree起動
    "vs" 'split-window-right                ; :vs
    "x"  'execute-extended-command)         ; M-x

  ;; Consult-ripgrepをカレントディレクトリのみを対象として実行
  (defun consult-ripgrep-current-directory ()
    "`consult-ripgrep' in current directory."
    (interactive)
    (consult-ripgrep default-directory))

  (defmacro general-set-which-key-name (mode &rest args)
    (let ((expr nil))
      (while args
        (let ((key (pop args))
              (name (pop args)))
          (push key expr)
          (push `'(:ignore t :which-key ,name) expr)))
      `(general-def ,mode
         :prefix "SPC" ,@(nreverse expr))))

  (general-set-which-key-name
   normal
   "f"  "flymake"
   "c"  "consult"
   "e"  "elisp"
   "g"  "move tab"
   "h"  "help"
   "l"  "lsp"
   "m"  "mode"
   "n"  "number"
   "o"  "org"
   "oe" "export"
   "w"  "window"
   "t"  "tab")

  ;; mod別設定
  (defmacro my/evil-define-keys (mode map &rest args)
    (let ((expr nil))
      (while args
        (push `(evil-define-key ,mode ,map (kbd ,(pop args)) ,(pop args)) expr))
      `(progn ,@(nreverse expr))))

  ;; org-modeだけgeneralで設定できないので
  (my/evil-define-keys 'normal org-mode-map
                       "SPC a s"   'org-archive-subtree
                       "SPC t t"   'org-todo
                       "SPC t i"   'org-clock-in
                       "SPC t o"   'org-clock-out
                       "SPC c c"   'org-toggle-checkbox
                       "SPC o e h" 'org-html-export-to-html ; htmlに出力
                       "SPC r f"   'org-roam-node-find      ; org-roam内でfind file
                       "SPC r b"   'org-roam-buffer-toggle  ; backlinksの切り替え
                       "SPC r g"   'org-roam-graph          ; グラフを作成して表示
                       "SPC r i"   'org-roam-node-insert    ; リンクを挿入
                       "SPC z f"   'org-cycle)

  (my/evil-define-keys 'normal markdown-mode-map
                       "SPC o e h" 'markdown-export        ; htmlに出力
                       "SPC r f"   'org-roam-node-find     ; org-roam内でfind file
                       "SPC r b"   'org-roam-buffer-toggle ; backlinksの切り替え
                       "SPC r g"   'org-roam-graph         ; グラフを作成して表示
                       "SPC r i"   'org-roam-node-insert)  ; リンクを挿入

  (my/evil-define-keys 'normal org-mode-map
                       "M-o" 'org-meta-return
                       "TAB" 'org-cycle)

  (my/evil-define-keys 'normal easy-jekyll-mode-map
                       "n" 'easy-jekyll-newpost
                       "a" 'easy-jekyll-article
                       "p" 'easy-jekyll-preview
                       "P" 'easy-jekyll-publish
                       "o" 'easy-jekyll-open
                       "d" 'easy-jekyll-delete
                       "c" 'easy-jekyll-open-config
                       "v" 'easy-jekyll-view
                       "g" 'easy-jekyll-refresh
                       "s" 'easy-jekyll-sort-time
                       "S" 'easy-jekyll-sort-char
                       "q"  'evil-delete-buffer)

  (general-def insert
    "C-k" nil
    "C-y" nil
    "C-n" nil
    "C-p" nil
    "C-x C-j" 'skk-mode
    "C-j" 'skk-insert)
  ;; TODO: C-eで行末に移動するようにする
  ;; TODO: C-aで行頭い移動するようにする

  (my-leader-def
    :keymaps 'dired-mode-map
    "mw" 'wdired-change-to-wdired-mode  ; ファイル名を直接編集
    "mcf" 'dired-copy-filename-as-kill  ; ファイル名をコピー
    "mcp" 'dired-copy-fullpass-as-kill) ; フルパスをコピー

  (with-eval-after-load 'paredit
    (general-def insert
      :keymaps 'paredit-mode
      "C-j" 'skk-insert)) ; paredit-modeでもSKKが使えるように
  )

;; 最後のカーソル位置を保存する
(use-package saveplace
  :config
  (save-place-mode))

;; ファイルの閲覧履歴を保存する
(use-package recentf
  :custom
  (recentf-max-saved-items 1024) ;; 保存する履歴の最大数
  :config
  (recentf-mode))

;; コマンドの履歴を保存
(use-package savehist
  :init
  (savehist-mode))

;; clojure-mode
(use-package clojure-mode)

(use-package clojure-ts-mode
  :after clojure-mode
  :hook
  (clojure-mode . clojure-ts-mode))

;; csharp-mode
(use-package csharp-mode)

;; paredit
(use-package paredit
  :hook
  ((emacs-lisp-mode clojure-mode) . paredit-mode))

;; 他プロセスの編集をバッファに反映
(use-package visible-auto-revert
  :functions visible-auto-revert-mode
  :vc ( :url "https://github.com/kn66/visible-auto-revert.el"
        :rev :newest)
  :config
  (visible-auto-revert-mode))

;; camelCase単位で移動する
(use-package subword
  :config
  (global-subword-mode))

;; 長い行を含むファイルの最適化
(use-package so-long
  :config
  (global-so-long-mode))

;; リージョンがアクティブな状態で文字を入力した際、
;; 選択範囲を削除してから挿入
(use-package delsel
  :config
  (delete-selection-mode))

;; フレーム内のウィンドウ構成をタブとして管理する機能
(use-package tab-bar
  :config
  (tab-bar-mode)
  (tab-bar-history-mode))

;; プロジェクト管理機能
(use-package project
  :custom
  (project-vc-extra-root-markers '(".dir-locals.el" "package.json")))

;; プレフィックスキーをなしで連続実行
(use-package repeat
  :config
  (repeat-mode))

;; シンタックスチェックフレームワーク
(use-package flymake
  :hook ((prog-mode
          conf-mode) . flymake-mode)
  :init
  (setopt flymake-indicator-type nil
          flymake-show-diagnostics-at-end-of-line t))

;; Gnusをメールリーダーとして使用する
(use-package gnus
  :functions gnus-demon-init
  :after bbdb
  :config
  (gnus-demon-init))

;; bbdb
(use-package bbdb
  :functions bbdb-initialize bbdb-mua-auto-update-init bbdb-insinuate-gnus
  :init
  (setopt bbdb-file (locate-user-emacs-file "bbdb")
          bbdb-mua-auto-action 'create
          bbdb-mua-action 'create
          bbdb-mua-pop-up nil
          bbdb-add-name t                           ; 既存レコードの名前変更を自動承認
          bbdb-add-aka t                            ; 別名追加を自動承認
          bbdb-message-all-address t
          bbdb-message-try-all-headers t
          bbdb-completion-display-record nil
          bbdb-complete-mail-allow-cycling t)
  :config
  (bbdb-initialize 'gnus 'message)
  (bbdb-mua-auto-update-init 'gnus 'message)
  (add-hook 'gnus-startup-hook #'bbdb-insinuate-gnus))

;; 新着メールが届いた際にデスクトップ通知
(use-package gnus-desktop-notify
  :functions gnus-desktop-notify-mode gnus-demon-add-rescan
  :config
  (gnus-desktop-notify-mode)
  (gnus-demon-add-rescan))

;; edff
(use-package ediff
  :custom
  (ediff-split-window-function 'split-window-horizontally))

;; org-mode
(defconst my-org-dir "D:/shiyo/iCloudDrive/iCloud~md~obsidian/Obsidian")
(defconst my-work-dir "D:/shiyo/Work/")

(use-package org
  :init
  (let ((todo (concat my-org-dir "/todo"))
        (todo-file (concat my-org-dir "/todo/todo.org")))
    (setopt org-return-follows-link t
            org-todo-keywords      '((sequence "TODO" "WIP" "|" "DONE")
                                     (sequence "REMD" "|" "FIN"))
            org-todo-keyword-faces '(("WIP" . "orange") ("DONE" . "green"))
            org-directory my-org-dir
            org-download-image-dir (concat my-org-dir "/Images/")
            org-agenda-files `(,todo)
            org-agenda-current-time-string "← now"
            org-agenda-time-grid
            '((daily today require-timed)
              (0900 01000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100 2200 2300 2400)
              "-"
	          "------------------------------")
            org-agenda-block-separator "============================================================"

            org-capture-templates `(("t" "Todo" entry
                                     (file+headline ,todo-file "[#C] REMINED")
                                     "* REMD [#C] %? (wrote on %U)"))
            org-archive-location "%s_archive::* Finished"
            org-roam-file-extensions '("org" "md")
            turn-follows-link t))          ; Returnキーでリンク先を開く
  )

;; org-indent
(use-package org-indent
  :ensure nil
  :hook (org-mode . org-indent-mode))

;; org-roam
(use-package org-roam
  :bind (("C-c n l" . org-roam-buffer-toggle)
         ("C-c n f" . org-roam-node-find)
         ("C-c n g" . org-roam-graph)
         ("C-c n i" . org-roam-node-insert)
         ("C-c n c" . org-roam-capture)
         ("C-c n j" . org-roam-dailies-capture-today))
  :init
  (setopt org-roam-directory (file-truename my-org-dir)
          org-roam-dailies-directory (concat my-org-dir "/DailyNote/")
          org-roam-index-file (concat my-org-dir "/Home.md")
          org-roam-db-location (concat my-work-dir "/org/database.db")
          org-roam-db-update-method 'immediate
          org-roam-mode-sections '(org-roam-backlinks-section
                                   org-roam-reflinks-section
                                   ;; org-roam-unlinked-references-section ; 重いらしい
                                   ))
  :config
  (require 'org-roam-export)
  (require 'org-roam-protocol)
  (setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag))))

(defun my/md-roam-assign-filename-as-id ()
  "ファイル名をIDとしてFrontmatterに付与する.
必ずファイルが存在するディレクトリの上でやること。"
  (interactive)
  (let ((files (directory-files-recursively org-roam-directory "\\.md$")))
    (dolist (file files)
      (with-current-buffer (find-file-noselect file)
        (let ((id-candidate (file-name-base file))) ; ファイル名をID候補にする
          (goto-char (point-min))
          ;; id: 行があるか確認
          (unless (save-excursion (re-search-forward "^id: " nil t))
            (goto-char (point-min))
            (if (re-search-forward "^---$" nil t)
                ;; すでにFrontmatterがある場合
                (progn
                  (forward-line 1)
                  (insert "id: " id-candidate "\n"))
              ;; Frontmatterがない場合、新規作成
              (insert "---\ntitle: " id-candidate "\nid: " id-candidate "\n---\n\n"))
            (save-buffer)))
        (kill-buffer)))))

;; org-roam-ui
(use-package org-roam-ui
  :after org-roam
  :config
  (setopt org-roam-ui-sync-theme t
          org-roam-ui-follow t
          org-roam-ui-update-on-save t
          org-roam-ui-open-on-start t))

;; md-roam
(use-package md-roam
  :after org-roam
  :functions md-roam-mode org-roam-db-autosync-mode
  :vc (md-roam :url "https://github.com/nobiot/md-roam" :rev :newest)
  :config
  (md-roam-mode 1)
  (setopt md-roam-file-extension "md")
  (org-roam-db-autosync-mode)) ; autosync-mode は db-sync をトリガーします。md-roam-mode はすでに有効になっている必要があります

(setopt org-roam-capture-templates
        '(("d" "default" plain "%?" :target
           (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
                      "#+title: ${title}\nid: ${title}\n")
           :unnarrowed t)

          ("m" "Markdown" plain "" :target
           (file+head "%<%Y-%m-%dT%H%M%S>-${slug}.md"
                      "---\ntitle: ${title}\nid: ${title}\n---\n")
           :unnarrowed t)))

;; obsidian
(use-package obsidian
  :config
  (setopt obsidian-directory my-org-dir
          obsidian-inbox-directory (concat my-org-dir "/DailyNote")
          obsidian-daily-note-template (concat my-org-dir "/Template/DailyNote.md"))
  (global-obsidian-mode t))

;; powershell
(use-package powershell)

;; denote
;;(use-package denote
;;  :config
;;  ;; テキストモードでもDenoteのリンクをハイライト
;;  (add-hook 'text-mode-hook #'denote-fontify-links-mode)
;;  ;; DiredでDenoteディレクトリを開いた際にDenote関連の機能を利用可能に
;;  (add-hook 'dired-mode-hook #'denote-dired-mode-in-directories)
;;  ;; コンテキストメニュー(右クリックメニュー)にDenoteの操作を追加
;;  (add-hook 'context-menu-funcions #'denote-context-menu)
;;  ;; バッファ名をDenoteノートのタイトルに合わせて自動的にリネーム
;;  (denote-rename-buffer-mode))

;; ddskk
(use-package ddskk
  :bind ("C-j"    . skk-mode)
  :hook (org-mode . skk-mode)
  :init
  (setopt skk-large-jisyo             "D:/shiyo/skk/SKK-JISYO.geo"
          skk-extra-jisyo-file-list '("D:/shiyo/skk/SKK-JISYO.jinmei"
                                      "D:/shiyo/skk/SKK-JISYO.L"
                                      "D:/shiyo/skk/SKK-JISYO.law"
                                      "D:/shiyo/skk/SKK-JISYO.propernoun")))

;; corfu
;; Emacsの補完UIを強化するパッケージ
(use-package corfu
  :bind ( :map corfu-map
          ("RET"      . nil)
          ("<return>" . nil)
          ("SPC"      . corfu-insert-or-escape-separator))
  :init
  (setopt corfu-cycle t
          corfu-auto t
          corfu-auto-prefix 1
          corfu-auto-delay 0.0
          corfu-preselect 'directory
          corfu-quit-no-match t
          corfu-on-exact-match 'show)

  (defun corfu-insert-or-escape-separator ()
    "Rotate prompt between inserting, escaping and removing the separator.
An alternative to `corfu-insert-separator`, useful in cases where including the
separator in the query is important, and the user wants a single bind for
everything. It can be called repeatedly, and will do, in order
    (starting from  a promt without any separators):

1. Insert the separator character at prompt end.
2. Insert the backslash character before the inserted separator, to treat it as
part of the prompt instead of as a split point for components.
3. Remove the separator and backslash.

The sequence will loop to 1 if called further, allowing to fix accidental
changes to the prompt. Note this function can remove the separator character."
    (interactive)
    (if (char-equal (char-before) corfu-separator)
        (if (char-equal (char-before (1- (point))) ?\\)
            (save-excursion (delete-char -2))                   ;; Case 3
          (save-excursion (backward-char 1) (insert-char ?\\))) ;; Case 2
      (call-interactively #'corfu-insert-separator)))          ;; Case 1
  (global-corfu-mode)
  (corfu-popupinfo-mode)
  (corfu-history-mode))

;; corfu向けEmacs標準機能の設定
(use-package emacs
  :custom
  ;; Tabを押したときインデントの後に補完する
  (tab-always-indent 'complete))

;; 標準補完インターフェースの拡張
(use-package cape
  :functions cape-wrap-buster cape-wrap-nonexclusive
  cape-wrap-noninterruptible cape-dabbrev cape-file cape-keyword
  cape-elisp-block
  :config
  (advice-add 'eglot-completion-at-point :around #'cape-wrap-buster)
  (advice-add 'eglot-completion-at-point :around #'cape-wrap-nonexclusive)
  (advice-add 'lsp-completion-at-point   :around #'cape-wrap-buster)
  (advice-add 'lsp-completion-at-point   :around #'cape-wrap-nonexclusive)
  (advice-add 'lsp-completion-at-point   :around #'cape-wrap-noninterruptible)

  (add-hook 'completion-at-point-functions #'cape-dabbrev)
  (add-hook 'completion-at-point-functions #'cape-file)
  (add-hook 'completion-at-point-functions #'cape-keyword)
  (add-hook 'completion-at-point-functions #'cape-elisp-block)
  (add-hook 'completion-at-point-functions #'tempel-complete))

;; ターミナル環境でもcorfu
(use-package corfu-terminal
  :functions corfu-terminal-mode
  :if (version< emacs-version "31")
  :after corfu
  :unless (display-graphic-p)
  :config
  (corfu-terminal-mode))

;; Cofu保管候補の複合ソート
(with-eval-after-load 'corfu
  (defun my-corfu-combined-sort (candidates)
    "display-sort-functionとcorfu-sort-functionの両方を使用して候補(CANDIDATES)をソート"
    (let ((candidates
           (let ((display-sort-func (corfu--metadata-get 'display-sort-funtion)))
             (if display-sort-func
                 (funcall display-sort-func candidates)
               candidates))))
      (if corfu-sort-function
          (funcall corfu-sort-function candidates)
        candidates)))

  ;; corfuのデフォルトソート関数を上記カスタム関数で上書き
  (setopt corfu-sort-override-function #'my-corfu-combined-sort))

;; 保管候補やフィルター機能
(use-package prescient
  :custom
  (completion-category-overrides '((eglot (styles prescient basic))
                                   (eglot-capf (styles prescient basic))))
  :config
  (with-eval-after-load 'migemo
    (defun my/prescient-regexp-regexp-advice (orig-fun query &rest args)
      "Advice function to replace QUERY with (migemo-get-pattern query)."
      (let ((migemo-query (migemo-get-pattern query)))
        (apply orig-fun migemo-query args)))
    (advice-add 'prescient-regexp-regexp :around #'my/prescient-regexp-regexp-advice))
  (prescient-persist-mode))

;; prescientをverticoに適用
(use-package vertico-prescient
  :functions vertico-prescient-mode
  :after (vertico prescient)
  :config
  (vertico-prescient-mode))

;; prescientをcorfuに適用
(use-package corfu-prescient
  :functions corfu-prescient-mode
  :after (corfu prescient)
  :config
  (corfu-prescient-mode))

;; ミニバッファの補完UIを強化
(use-package vertico
  :functions vertico-mode
  :custom
  (vertico-resize t)
  :config
  (vertico-mode))

;; Emacs標準機能の設定
(use-package emacs
  :ensure nil
  :init
  (setopt enable-recursive-minibuffers t
          read-extended-command-predicate #'command-completion-default-include-p
          minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt))
  :config
  (context-menu-mode))

;; 直前に実行したミニバッファコマンドを再度呼び出せる
(use-package vertico-repeat
  :ensure nil
  :after vertico
  :bind ("M-R" . vertico-repeat)
  :hook (minibuffer-setup . vertico-repeat-save))

;; ディレクトリ補完を強化
(use-package vertico-directory
  :ensure nil
  :after vertico
  :bind ( :map vertico-directory-map
          ("C-h" . vertico-directory-up))
  :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))

;; 補完UIの表示形式をカテゴリごとにカスタマイズ
(use-package vertico-multiform
  :functions vertico-multiform-mode
  :ensure nil
  :after vertico
  :custom
  (vertico-multiform-categories
   '((file buffer grid (:keymap . vertico-directory-map))
     (imenu (:not indexed mouse))
     (symbol (vertico-sort-function . vertico-sort-alpha))))
  :config
  (vertico-multiform-mode))

;; マウス操作による補完候補の選択やスクロールが可能に
(use-package vertico-mouse
  :functions vertico-mouse-mode
  :ensure nil
  :after vertico
  :config
  (vertico-mouse-mode))

;;; EmacsからLLMを利用するための汎用インターフェース
;;; gptelを導入予定
;;; gptel-commitを導入予定
;;; copilot.elを導入予定

;; 絞り込みコマンド
;; Example configuration for Consult
(use-package consult
  :functions consult-register-window consult-xref consult-customize
  ;; Replace bindings. Lazily loaded by `use-package'.
  :bind (;; C-c bindings in `mode-specific-map'
         ("C-c M-x" . consult-mode-command)
         ("C-c h" . consult-history)
         ("C-c k" . consult-kmacro)
         ("C-c m" . consult-man)
         ("C-c i" . consult-info)
         ([remap Info-search] . consult-info)
         ;; C-x bindings in `ctl-x-map'
         ("C-x M-:" . consult-complex-command)     ;; orig. repeat-complex-command
         ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
         ("C-x 5 b" . consult-buffer-other-frame)  ;; orig. switch-to-buffer-other-frame
         ("C-x t b" . consult-buffer-other-tab)    ;; orig. switch-to-buffer-other-tab
         ("C-x p b" . consult-project-buffer)      ;; orig. project-switch-to-buffer
         ;; Custom M-# bindings for fast register access
         ("M-#" . consult-register-load)
         ("M-'" . consult-register-store)          ;; orig. abbrev-prefix-mark (unrelated)
         ("C-M-#" . consult-register)
         ;; Other custom bindings
         ("M-y" . consult-yank-pop)                ;; orig. yank-pop
         ;; M-g bindings in `goto-map'
         ("M-g e" . consult-compile-error)
         ("M-g r" . consult-grep-match)
         ("M-g g" . consult-goto-line)             ;; orig. goto-line
         ("M-g M-g" . consult-goto-line)           ;; orig. goto-line
         ("M-g o" . consult-outline)               ;; Alternative: consult-org-heading
         ("M-g m" . consult-mark)
         ("M-g k" . consult-global-mark)
         ("M-g i" . consult-imenu)
         ("M-g I" . consult-imenu-multi)
         ;; M-s bindings in `search-map'
         ("M-s d" . consult-find)                  ;; Alternative: consult-fd
         ("M-s c" . consult-locate)
         ("M-s g" . consult-grep)
         ("M-s G" . consult-git-grep)
         ("M-s L" . consult-line-multi)
         ("M-s k" . consult-keep-lines)
         ("M-s u" . consult-focus-lines)
         ;; Isearch integration
         ("M-s e" . consult-isearch-history)
         :map isearch-mode-map
         ("M-e" . consult-isearch-history)         ;; orig. isearch-edit-string
         ("M-s e" . consult-isearch-history)       ;; orig. isearch-edit-string
         ("M-s l" . consult-line)                  ;; needed by consult-line to detect isearch
         ("M-s L" . consult-line-multi)            ;; needed by consult-line to detect isearch
         ;; Minibuffer history
         :map minibuffer-local-map
         ("M-s" . consult-history)                 ;; orig. next-matching-history-element
         ("M-r" . consult-history))                ;; orig. previous-matching-history-element

  ;; The :init configuration is always executed (Not lazy)
  :init

  ;; Tweak the register preview for `consult-register-load',
  ;; `consult-register-store' and the built-in commands.  This improves the
  ;; register formatting, adds thin separator lines, register sorting and hides
  ;; the window mode line.
  (advice-add #'register-preview :override #'consult-register-window)
  (setopt register-preview-delay 0.5)

  ;; Use Consult to select xref locations with preview
  (setopt xref-show-xrefs-function #'consult-xref
          xref-show-definitions-function #'consult-xref)

  ;; Configure other variables and modes in the :config section,
  ;; after lazily loading the package.
  :config

  ;; Optionally configure preview. The default value
  ;; is 'any, such that any key triggers the preview.
  ;; (setq consult-preview-key 'any)
  ;; (setq consult-preview-key "M-.")
  ;; (setq consult-preview-key '("S-<down>" "S-<up>"))
  ;; For some commands and buffer sources it is useful to configure the
  ;; :preview-key on a per-command basis using the `consult-customize' macro.
  (consult-customize
   consult-theme :preview-key '(:debounce 0.2 any)
   consult-ripgrep consult-git-grep consult-grep consult-man
   consult-bookmark consult-recent-file consult-xref
   consult-source-bookmark consult-source-file-register
   consult-source-recent-file consult-source-project-recent-file
   ;; :preview-key "M-."
   :preview-key '(:debounce 0.4 any))

  ;; Optionally configure the narrowing key.
  ;; Both < and C-+ work reasonably well.
  (setopt consult-narrow-key "<") ;; "C-+"

  ;; Optionally make narrowing help available in the minibuffer.
  ;; You may want to use `embark-prefix-help-command' or which-key instead.
  ;; (keymap-set consult-narrow-map (concat consult-narrow-key " ?") #'consult-narrow-help)
  )

;; ディレクトリのナビゲーションと管理を支援
(use-package consult-dir
  :bind (("C-x C-d" . consult-dir)
         :map minibuffer-local-completion-map
         ("C-x C-d" . consult-dir)
         ("C-x C-j" . consult-dir-jump-file)))

;; 補完候補への注釈追加
;; Enable rich annotations using the Marginalia package
(use-package marginalia
  :functions marginalia-mode
  ;; Bind `marginalia-cycle' locally in the minibuffer. To make the binding
  ;; available in the *Completions* buffer, add it to the
  ;; `completion-list-mode-map'.
  :bind ( :map minibuffer-local-map
          ("M-A" . marginalia-cycle))
  ;; The :init section is always executed.
  :init

  ;; Marginalia must be activated in the :init section of use-package such that
  ;; the mode gets enabled right way. Note that this forces loading the package.
  (marginalia-mode))

;; コンテキストアクション
(use-package embark
  :functions embark-prefix-help-command
  :bind
  (("C-."  . embark-act)       ;; pick some comfortable binding
   ("C-;"  . embark-dwim)      ;; good alternative: M-.
   ("C-h B". embark-bindings)) ;; alternative for `describe-bindings'

  :init

  ;; Optionally replace the key help with a completing-read interface
  (setopt prefix-help-command #'embark-prefix-help-command)

  ;; Show the Embark target at point via Eldoc. You may adjust the
  ;; Eldoc strategy, if you want to see the documentation from
  ;; multiple providers. Beware that using this can be a little
  ;; jarring since the message shown in the minibuffer can be more
  ;; tahn one line, causing the modeline to move up and down:

  ;; (add-hook 'eldoc-documentation-functions #'embark-eldoc-first-target)
  ;; (setopt eldoc-documentation-strategy #'eldoc-documentation-compose-eagerly)

  ;; Add Embark to the mouse context menu. Also enable `context-menu-mode'.
  ;; (context-menu-mode 1)
  ;; (add-hook 'context-menu-functions #'embark-context-menu 100)

  :config

  ;; Hide the mode line of the Embark live/completions buffers
  (add-to-list 'display-buffer-alist
               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
                 nil
                 (window-parameters (mode-line-format . none)))))

;; EmbarkとConsultを連携させ、Consultの検索結果などからEmbarkの
;; アクションを実行できるように
(use-package embark-consult
  :hook (embark-collect-mode . consult-preview-at-point-mode))

;; 軽量スニペットエンジン
(use-package tempel
  :bind (("M-+" . tempel-complete) ; カーソル位置でスニペット候補を補完・展開 ( `tempel-expand'の代替としても)
         ("M-*" . tempel-insert))) ; 指定したスニペットを挿入

;; tempel用のスニペット定義集
(use-package tempel-collection
  :after tempel)

;;; Git
;; 高機能なGitクライアント
(use-package magit
  :init
  (setopt magit-log-section-commit-count 30)
  :config
  (require 'magit-extras)) ; Magitの追加機能(magit-plusなど)をロード

;; Gitリポジトリでの未コミットの変更箇所を視覚的に表示
(use-package diff-hl
  :functions global-diff-hl-mode global-diff-hl-show-hunk-mouse-mode diff-hl-margin-mode
  ;; Magitのバッファ更新前後にdiff-hlも更新
  :hook ((magit-pre-refresh . diff-hl-magit-pre-refresh)
         (magit-post-refresh . diff-hl-magit-post-refresh))
  :init
  (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)
  (global-diff-hl-mode)                 ; diff-hlモードをグローバルに有効化
  (global-diff-hl-show-hunk-mouse-mode) ; マウスオーバーで変更差分(hunk)を表示
  (diff-hl-margin-mode))                ; フリンジではなくマージン領域に差分表示

;; Emacsでdifftasticを使用できるように
(use-package difftastic
  :functions difftastic-bindings-mode
  :config
  (difftastic-bindings-mode))

;; フォント
(use-package fontaine
  :custom
  (fontaine-presets
   '((regular
      :default-family        "HackGen Console NF"
      :fixed-pitch-family    "HackGen Console NF"
      :variable-pitch-family "HackGen Console NF"
      :italic-family         "HackGen Console NF")
     (large
      :default-family        "HackGen Console NF"
      :variable-pitch-family "HackGen Console NF"))))

;;; modeline
;; マイナーモードがハンバーガーメニューに
(use-package minions
  :functions minions-mode
  :config
  (minions-mode))

;; モードラインににゃんこを表示
(use-package nyan-mode
  :functions nyan-mode
  :init
  (setopt nyan-bar-length 24
          nyan-animate-nyancat t)
  :config
  (nyan-mode))

;; キーバインドガイド
(use-package which-key
  :config
  (which-key-show-major-mode t)
  (which-key-mode))

;;; undo関連
(use-package vundo)

;; コードフォーマッタを統一的なインターフェースで利用可能にする
(use-package reformatter
  :functions reformatter-define
  :hook (((typescript-ts-mode
           tsx-ts-mode) . prettier-format-on-save-mode)
         ((csharp-mode
           csharp-ts-mode) . csharpier-format-on-save-mode))
  :config
  (reformatter-define prettier-format
    :program "npx"
    :args `("prettier" "--stdin-filepath" ,buffer-line-name)
    :lighter " PrettierFmt")

  (reformatter-define csharpier-format
    :program "csharpier"
    :args '("format" "--write-stdout")
    :lighter " CSharpFmt"))

;; SQL用のフォーマットパッケージ
(use-package sqlformat
  :init
  (setopt sqlformat-command 'sql-formatter
          sqlformat-args '("-l" "tsql")))

;; 選択範囲の段階的拡張
(use-package expreg
  :defer t
  :bind ("C-=" . expreg-expand))

;; 構造的操作ユーティリティ
(use-package puni
  :functions puni-global-mode
  :config
  (puni-global-mode))

;; ポップアップウィンドウ
(use-package popwin)

;; Tree-sitter関連
;; Tree-sitter文法管理
(use-package treesit-langs
  :functions treesit-langs-major-mode-setup
  :vc (treesit-langs :url "https://github.com/emacs-tree-sitter/treesit-langs" :rev :newest)
  :init
  ;; Emacs30でversion-mismatchが発生する場合は動作するバージョンを指定する
  ;; (setopt treesit-langs-bundle-version "0.12.300")
  :config
  ;; 各メジャーモードでTree-sitterを利用するための基本的な設定を自動で行う
  (treesit-langs-major-mode-setup))

;; Tree-sitter対応のメジャーモードを自動的に切り替える
(use-package treesit-auto
  :functions global-treesit-auto-mode
  :config
  (global-treesit-auto-mode))

;; Tree-sitterの構文解析情報を利用してコードの折り畳みをする
(use-package treesit-fold
  :functions global-treesit-fold-mode global-treesit-fold-indicators-mode
  :config
  (global-treesit-fold-mode)
  (global-treesit-fold-indicators-mode)

  ;; lsp-modeやflymakeとの競合を避けるため、オーバーレイ作成関数を上書き
  (with-eval-after-load 'treesit-fold-indicators
    (defun treesit-fold-indicators--create-overlay-at-point ()
      "Create indicator overlay at current point."
      (let* ((pos (line-beginning-position))
             (ov (make-overlay pos (1+ pos)))
             (window (selected-window)))
        (overlay-put ov 'treesit-fold-indicators-window window)
        (overlay-put ov 'window window)
        ;; overlayにkeymapを設定して優先させる
        (overlay-put ov 'keymap treesit-fold-indicators-mode-map)
        ov))))

;; カーソル下の単語を、snake_case, camelCase, UPPER_CASEの間で循環的に変換
(use-package string-inflection
  :init
  (defun my-string-inflection-cycle-auto ()
    "メジャーモードに応じて単語のケーススタイルを循環変換します。"
    (interactive)
    (cond
     ;; Emacs Lisp mode
     ((eq major-mode 'emacs-lisp-mode)
      (string-inflection-all-cycle)) ; すべてのスタイルを循環
     ;; Python mode
     ((eq major-mode 'python-mode)
      (string-inflection-python-style-cycle)) ; Pythonスタイル(snake_case, camelCase)
     ;; Java mode
     ((eq major-mode 'java-mode)
      (string-inflection-java-style-cycle)) ; Javaスタイル(camelCase, PascalCase)
     (t
      ;; デフォルト(Rubyスタイル)
      (string-inflection-ruby-style-cycle)))))

;; インライン翻訳
(use-package gt
  :defer t
  :config
  ;; 翻訳対象言語と言語のペアを設定 (英語 <-> 日本語)
  (setopt gt-langs '(en ja))
  ;; デフォルトの翻訳設定
  (setopt gt-default-translator
          (gt-translator
           ;; 翻訳対象のテキスト取得方法: 現在のバッファから段落単位で取得
           :taker
           (gt-taker :text 'buffer :pick 'paragraph)
           ;; 使用する翻訳エンジン: Google翻訳
           :engines
           (list (gt-bing-engine))
           ;; 翻訳結果の表示方法: 新しいバッファに表示
           :render
           (gt-insert-render))))

;; ローマ字による日本語検索
(use-package migemo
  :init
  ;; Migemoコマンドの設定 (cmigemoをデフォルトとして使用)
  ;; scoopなどでcmigemoを入れる必要がある
  (setopt migemo-command "C:/Users/shiyo/AppData/Roaming/.emacs.d/cmigemo/cmigemo")
  (setopt migemo-options '("-q" "--emacs"))

  ;; Migemo辞書のパス設定
  (setopt migemo-dictionary
          (expand-file-name "C:/Users/shiyo/AppData/Roaming/.emacs.d/cmigemo/dict/utf-8"
                            (getenv "USERPROFILE")))

  (setopt migemo-user-dictionary nil)
  (setopt migemo-regex-dictionary nil)
  (setopt migemo-coding-system 'utf-8-unix)

  :config
  (migemo-init))

;; 検索数の表示
(use-package anzu
  :after migemo
  :config
  (global-anzu-mode +1)
  (setq anzu-use-migemo t)
  (setq anzu-search-threshold 1000)
  (setq anzu-minimum-input-length 3)
  (set-face-attribute 'anzu-mode-line nil
                      :foreground "yellow" :weight 'bold))

;; プロジェクトごとに異なるコードスタイルを設定するためのファイル
;; .editorconfigのサポート
(use-package editorconfig
  :config
  (editorconfig-mode))

;; grep結果の直接編集
(use-package wgrep)

;; 画面に余白を
(use-package spacious-padding
  :config
  ;; These are the default values, but I keep them here for visibility.
  ;; Also check `spacious-padding-subtle-frame-lines'.
  (setq spacious-padding-widths
        '( :internal-border-width 15
           :header-line-width 4
           :mode-line-width 6
           :custom-button-width 3
           :tab-width 4
           :right-divider-width 30
           :scroll-bar-width 8
           :fringe-width 8))

  (spacious-padding-mode 1))

;; バッファの先頭にパンくずリストを表示
(use-package breadcrumb
  :config
  (breadcrumb-mode))

;; カーソル位置から離れた場所にある単語やシンボルへ素早くジャンプする
(use-package avy
  :bind ("C-:" . avy-goto-char))

;; カーソルの移動や検索操作を視覚的に強調表示
(use-package pulsar
  :config
  (pulsar-global-mode))

;; コードの変更箇所を一時的にハイライト
(use-package goggles
  :hook ((prog-mode text-mode) . goggles-mode)
  :config
  (setq-default goggles-pulse t))

;; 自動インデント検出と設定
(use-package dtrt-indent
  :config
  (dtrt-indent-global-mode))

;; コードのインデントをリアルタイムで自動的に調整
(use-package aggressive-indent
  :hook (emacs-lisp-mode . aggressive-indent-mode))

;; コードのインデントレベルを視覚的に示す縦線
(use-package indent-bars
  :init
  (dolist (hook '(python-base-mode-hook
                  yaml-mode-hook
                  typescript-ts-mode-hook
                  tsx-ts-mode-hook
                  js-ts-mode-hook
                  json-ts-mode-hook
                  java-mode-hook
                  java-ts-mode-hook
                  csharp-ts-mode-hook))
    (add-hook hook
              ;; hookで有効にすると適切なインデントで描画されないことがあることへの対処
              (lambda ()
                (run-with-timer 3 nil #'indent-bars-mode)))))

;; エディタの左右に均等な余白を追加し、テキストの可読性を向上
(use-package perfect-margin
  :config
  (perfect-margin-mode))

;; 縦分割を優先
(setopt split-height-threshold nil
        split-width-threshold 128)

;; lspクライアント
(use-package lsp-mode
  :hook ((html-ts-mode
          typescript-ts-mode
          tsx-ts-mode
          js-ts-mode
          json-ts-mode
          java-mode
          java-ts-mode
          csharp-ts-mode
          clojure-ts-mode) . lsp)
  :init
  (setopt lsp-completion-provider :none
          lsp-keymap-prefix "M-l"
          lsp-headerline-breadcrumb-enable nil)
  (add-hook 'lsp-completion-mode-hook
            (lambda ()
              (setq-local completion-category-defaults
                          (assq-delete-all 'lsp-capf completion-category-defaults)))))

;; lsp-ui
(use-package lsp-ui)

;; lspサーバから提供されるシンボル情報をConsultのインターフェースで検索・参照
(use-package consult-lsp
  :after lsp-mode
  :config
  (define-key lsp-mode-map [remap xref-find-apropos] #'consult-lsp-symbols))

;; lspサーバから取得したスニペット情報をTempelスニペットエンジンと連携して利用
(use-package lsp-snippet
  :vc ( :url "https://github.com/svaante/lsp-snippet"
        :rev :newest)
  :config
  (with-eval-after-load 'lsp-mode
    (lsp-snippet-tempel-lsp-mode-init))
  (with-eval-after-load 'eglot
    (lsp-snippet-tempel-eglot-init)))

;; アイコンフォントでEmacsのUI要素にアイコンを表示
;; 初回はM-x all-the-icons-install-fontsが必要
(use-package all-the-icons
  :if (display-graphic-p))

;;; プログラミング言語関連モード
;; elisp自動フォーマット
(use-package elisp-autofmt
  :hook (emacs-lisp-mode . elisp-autofmt-mode))

;; Mermaid気泡でダイアグラムを記述するためのメジャーモード
(use-package mermaid-mode
  :config
  (setopt mermaid-flags "--scale 2"))
(use-package mermaid-ts-mode)

;;; Web開発関連
;; HTMLやCSSなどの記述を効率化
(use-package emmet-mode
  :hook ((html-ts-mode
          css-ts-mode) . emmet-mode))

;; ORMツールPrismaのスキーマファイル(.prisma)の編集をサポートするメジャーモード
(use-package prisma-mode
  :vc ( :url "https://github.com/pimeys/emacs-prisma-mode" :rev :newest))

;; markdown用メジャーモード
(use-package markdown-mode
  :init
  (setopt markdown-enable-wiki-links t
          markdown-hide-markup nil)
  :config
  (add-hook 'markdown-mode-hook #'(lambda () (setopt evil-shift-width 2))))

;; jekyll
(use-package easy-jekyll
  :init
  (add-to-list 'exec-path "C:/Ruby34-x64/bin")
  (setopt easy-jekyll-basedir "D:/shiyo/Work/Web/portfolio/"
          easy-jekyll-url "https://book-of-durrow.github.io/portfolio/"
          ;; easy-jekyll-sshdomain ""
          ;; easy-jekyll-root ""
          easy-jekyll-previewtime 300
          easy-jekyll-post-layout "post"
          easy-jekyll-image-directory "assets"
          easy-jekyll-preview-url "http://124.211.211.47:4000/portfolio/"))


;;; その他

;;; 自作関数
;; DBから抽出したデータをIN句用に変換
(defun my/replace-region-with-in-clause (start end)
  "選択範囲 (START から END まで)の内容をSQLのIN句に変換し、元の選択範囲を置換する."
  (interactive "r")       ; リージョンがアクティブな場合に呼び出し可能
  (let* ((region-content (buffer-substring-no-properties start end)) ; 選択範囲の文字列を取得
         (records (split-string region-content "\n" t))              ; 改行で分割してリスト化(空行は無視)
         (unique-records (delete-dups records))                      ; 重複を削除
         ;; 各レコードをシングルクォートで囲む
         (quoted-records (mapcar (lambda (record) (format "'%s'" record)) unique-records))
         ;; IN句の文字列を生成
         (in-clause (format "IN (%s)" (mapconcat 'identity quoted-records ", "))))
    (delete-region start end)           ; 元の選択範囲を削除
    (insert in-clause)))      ; 生成したIN句を挿入

;; Insert today's date.
(defun today ()
  "今日の日付を入力する."
  (interactive)
  (insert (format-time-string "%Y-%m-%d" (current-time))))

;; hydraを導入するかもしれない(ちょっと調べてからにする)

;; restart-emacsは29からデフォルト装備になった?

;;; hook

;; Config for latex which there is the function which delete \n after japanese char.
(autoload 'org-mode "latex-config" nil t)

;; parse csv file.
(autoload 'parse-csv-string-rows "parse-csv" nil t)
(autoload 'parse-csv-string      "parse-csv" nil t)
(autoload 'parse-csv->list       "parse-csv" nil t)

(setq file-name-handler-alist my/saved-file-name-handler-alist)

;; (profiler-report)
;; (profiler-stop)

(provide 'init)
;;; init.el ends here