Emacs:複数のテーマを切り替えて使う

Emacsを快適に使うためにいろいろ自分好みのテーマを模索します。私の場合、deeper-blue misterioso material と変遷し、今は doom-dracula を使っています。

それぞれ特徴がありコードを書く時、文章を書く時等々、その時々で気分転換を兼ねて切り替えて使えたら面白いと思ってググってみたところ下記サイトのQ&Aで面白いTipsを見つけたので試して見ました。結構快適で愉しいのでご紹介します。

設定

(setq my-themes (list 'doom-dracula 'material 'misterioso 'deeper-blue)) 
(setq curr-theme my-themes)
(defun my-theme-cycle ()
  "Cycle custom theme."
    (interactive)
    (disable-theme (car curr-theme)) 
    (setq curr-theme (cdr curr-theme))
    (if (null curr-theme) (setq curr-theme my-themes))
    (load-theme (car curr-theme) t)
    (message "%s" (car curr-theme)))
(global-set-key [f7] 'my-theme-cycle)
(setq curr-theme my-themes)
(load-theme (car curr-theme) t)

DashboardにQiitaの新着リスト(tag:emacs)を表示させる

Alt Text

下記のTipsを参考にしてEmacsのDashboard画面にQiitaの新着一覧を表示させる事ができました。

設定

この設定例では、emacsタグで絞り込んだ一覧を表示するようにカスタマイズしています。

;; Use request.el for API call
(use-package request)

;; Item display function
(defun dashboard-qiita-insert-list (list-display-name list)
  "Render LIST-DISPLAY-NAME and items of LIST."
  (dashboard-insert-heading list-display-name)
  (mapc (lambda (el)
          (insert "\n    ")
          (widget-create 'push-button
                         :action `(lambda (&rest ignore)
                                    (browse-url ,(cdr (assoc 'url el))))
                         :mouse-face 'highlight
                         :follow-link "\C-m"
                         :button-prefix ""
                         :button-suffix ""
                         :format "%[%t%]"
                         (decode-coding-string (cdr (assoc 'title el)) 'utf-8))) list))

;; Function to get and display articles
(defun dashboard-qiita-insert (list-size)
  "Add the list of LIST-SIZE items from qiita."
  (request
   ;; (format "https://qiita.com/api/v2/items?page=1&per_page=%s" list-size)
   (format "https://qiita.com/api/v2/tags/emacs/items?page=1&per_page=%s" list-size)
   :sync t
   :parser 'json-read
   :success (cl-function
             (lambda (&key data &allow-other-keys)
               (dashboard-qiita-insert-list "Qiita(emacs-tag):" data)))))

;; add an article to the dashboard
(add-to-list 'dashboard-item-generators '(qiita . dashboard-qiita-insert))
(setq dashboard-items '((qiita . 15)))

参考

Emacs:Dashboardの詳細設定はGithubに公開しています。

Emacsのウインドウ操作の履歴をUndo/Redoする:winner-mode

emacsでいろいろ作業していると意図しない形で勝手にウインドウズが分割されたりします。設定して “C-g” で隠せるものもありますが、それも出来ないときは、“C-x 0” や “C-x 1” のお世話になります。

(winner-mode)
(bind-key "C-<left>" 'winner-undo)
(bind-key "C-<right>" 'winner-redo)
(winner-mode)
(bind-key "C-c <left>" 'winner-undo)
(bind-key "C-c <right>" 'winner-redo)

Scratch bufferをKILLさせない:emacs-lock-mode

わざわざscratch bufferをkillする人はいないと思いますが、“kill-other-windows” などを定義していると、うっかりscratchも一緒に消えてしまって困ることがありますね。

あやまってscratch killしたら新しいscratchを自動再生してくれるとか、kill出来ないようにする…というTipsも多いのですが、そんな回りくどいことをしなくてEmacsの標準機能だけて簡単に対応できることがわかりました。

emacs-lock-mode を使う##

パッケージも何もいりません。下記を設定してみてください。私は *Messages* も一緒に設定しています。

;; Set buffer that can not be killed.
(with-current-buffer "*scratch*"
  (emacs-lock-mode 'kill))
(with-current-buffer "*Messages*"
  (emacs-lock-mode 'kill))

この選定を有効にしたあとscratchをkill bufferしようとすると、

Buffer "*scratch*" is locked and cannot be killed

と叱られます。つまりkillされないようにロックが掛けられるのです。 emacs-lock-mode というのはもともとemacsの内蔵コマンドにあるのです。

Scratchを自動保存する

私もそうなのですが、ちょこっとした付箋的なメモをするためにscratchを使うという人も多いと思います。そんな人には、Emacsを再起動させても終了前のscratchが復帰してほしい…というニーズがあります。こうした目的のためのパッケージも何種類かありますが、私は、auto-seve-buffers-enhanced.el の機能を流用しています。このパッケージを使っているEmacserは多いと思うので、わざわざ専用のパッケージを使わずともscratchの保存再生が可能になります。

(use-package auto-save-buffers-enhanced)
;; Suppress Wrote's message
(setq auto-save-buffers-enhanced-quiet-save-p t)
;; Scratch buffer is also saved automatically
(setq auto-save-buffers-enhanced-save-scratch-buffer-to-file-p t)
(setq auto-save-buffers-enhanced-file-related-with-scratch-buffer "~/Dropbox/etc/emacs/scratch")
(auto-save-buffers-enhanced t)

scratchを瞬時にpopup表示させる

scratch-pop という便利なパッケージもあるのですが、私にとって余り必要と思わない機能もついているのでシンプルに設定を書いてみました。

popwinを導入した上で下記をinitファイルに書き、適当なキーバインドを設定すると一発でscratch bufferがpopupし、“C-g” で隠れてくれます。

(defun my:pop-scratch ()
  "Popup the scratch buffer."
  (interactive)
  (setq popwin:special-display-config '(("*scratch*")))
  (display-buffer "*scratch*"))

どんな使い方をしているか

簡単なelispの評価にscratchを使うのは当然ですが、私の場合、なにか調べたいキーワードをscratchにちょこっと書いてsearch-webで即検索…というような使い方を良くします。参考に私のhydra-search-webの設定にリンクを張っておきます。

Emacsからカレントディレクトリでターミナルアプリを開く

私の場合は、Emacsからターミナル作業をするときは基本eshellを使うようにしています。ただ、ごくたまに別窓でターミナルアプリを立ち上げてemacsと併行して作業したいときがありますので以下のように設定しています。

当然ながらコマンドラインでアプリが起動できるようにbashなりzshなりで設定しておくことが前提です。

macのiterm.appを起動させる設定

;; Launch iterm.app with Current buffer
(defun my:iterm-app ()
  "Open iterm.app with current dir."
  (interactive)
  (let ((dir default-directory))
    (shell-command (concat "open -a iterm.app " dir))))

HydraでEmacsのキーバインド問題を解消する

できるだけデフォルトを上書きしないようにと配慮しながらEmacsのキーバインドを考えるとなるとなかなか難しいですね。うまく割り当てできたとしてもとても覚えて使いこなすのは至難です。その問題を解消して視覚的で使いやすいUIを提供してくれるのが Hydra です。

Hydra はとても奥が深くまだまだ使いこなせないのですが、勉強を兼ねていろいろ設定しましたので備忘録を兼ねてご紹介します。こうすればなお良い…というようなアドバイスを頂けると嬉しいです。

設定の大部分は、hydraの作者であるabo-abo氏のTipsを参考にしました。

Hydra-melpa

Alt Text

emacsのパッケージ管理はいろいろあるようですが、私はMelpaを利用して、必要なパッケージは、init.el に書いて自動インストールされるようにしているのですが、メンテナンス(package-install、update、remove) のためのコマンドをhydraで設定しています。

M-x package-list-packege でリストを表示させてもいいのですが “U” で update すると全てが対象になって思わぬトラブルに遭遇することもあるので、package-utils というパッケージを使っています。

;; use package-utils.el
(defhydra hydra-melpa (:color red :hint nil)
   "
Package: _u_pdate  _r_emove  _i_nstall"
   ("u" package-utils-upgrade-by-name)
   ("r" package-utils-remove-by-name)
   ("i" package-install))

Hydra-git-gutter

Alt Text

emacsの設定ファイル群は、Gitで管理していますので、magit は必要不可欠です。

編集中は、git-gutterが便利なのでまとめてhydraに設定しています。

;; 02_git.el --- 02_git.el
;;; Commentary:
;;; Code:
;; (setq debug-on-error t)

;; magit
(autoload 'magit-status "magit" nil t)
(defadvice magit-status (around magit-fullscreen activate)
  "Magit-status always in fullr screen."
  (window-configuration-to-register :magit-fullscreen)
  ad-do-it
  (delete-other-windows))
(bind-key [f8] 'magit-status)

;; git-gutter
(global-git-gutter-mode t)
(defun git-gutter:toggle-popup-hunk ()
  "Toggle git-gutter hunk window."
  (interactive)
  (if (window-live-p (git-gutter:popup-buffer-window))
      (delete-window (git-gutter:popup-buffer-window))
    (git-gutter:popup-hunk)))

;; Hydra
(bind-key
 "s-g"
 (defhydra hydra-git-gutter (:color red :hint nil)
   "
_m_agit  _b_lame  _d_ispatch  _t_imemachine  |  hunk: _p_revious  _n_ext  _s_tage  _r_evert  pop_u_p  _SPC_:toggle"
   ("m" magit-status :exit t)
   ("b" magit-blame :exit t)
   ("t" git-timemachine :exit t)
   ("d" magit-dispatch :exit t)
   ("p" git-gutter:previous-hunk)
   ("n" git-gutter:next-hunk)
   ("s" git-gutter:stage-hunk)
   ("r" git-gutter:revert-hunk)
   ("u" git-gutter:popup-hunk)
   ("SPC" git-gutter:toggle-popup-hunk)))

;; Local Variables:
;; byte-compile-warnings: (not free-vars callargs)
;; End:
;;; 02_git.el ends here

Hydra-counsel

Alt Text

Hydraで複数のmakeコマンドを自在に使う

ホームページ管理のためにEmacsをワークデスクとして色んな作業をしています。その中で何度も繰り返し行う作業はMakeファイルに書いて限りなく自動化して、それをEmacsから実行しています。

M-x compile

とすると、

Compile command: make -k

とミニバッファーにでます。ここでリターンキーを押すと、makeコマンドが実行されます。

makeファイルの中の特定のブロックを実行したい時、例えば更新されたファイルをサーバーにアップロードしてホームページを更新する時は、make -k を消して make upftp と書き換えてリターンキーを押すとupftpコマンドが実行されるという仕組みです。

通常この使用法でも大した問題はないのですが、compile-commandの内容を書き換えて実行すると、次回にM-x compileコマンドを実行したとき make -k にはならず、先程の書き換えたものがデフォルトに変わってしまうのです。

そこで いろいろな引数でmakeコマンドを使い分けるためい、hydraで設定してみました。

設定

;;; 50_hydra-compile.el --- 50_hydra-compile.el
;;; Commentary:
;;; Code:
;;(setq debug-on-error t)

(bind-key
 [f2]
 (defhydra hydra-compile (:color red :hint nil)
  "
╭─────────────────────────────────────┐
│ 🗿 Compile:
     make:_a_ll  _u_pftp  _m_ove  _b_klog  _g_it
     ---------------------------------
     make:-_k_   _c_lean
└──────────────────────────────────────┘"
   ("a" my:make-all :exit t)
   ("u" my:make-upftp :exit t)
   ("m" my:make-move :exit t)
   ("b" my:make-bklog :exit t)
   ("g" my:make-git :exit t)
   ("k" my:make-default)
   ("c" my:make-clean)))

(defun my:make-default ()
  "Make command default."
  (interactive)
  (setq compile-command "make -k")
  (my:compile))

(defun my:make-upftp ()
  "Make command for upftp."
  (interactive)
  (setq compile-command "make up")
  (my:compile))

(defun my:make-all ()
  "Make command for all."
  (interactive)
  (setq compile-command "make -k && make up")
  (my:compile))

(defun my:make-move ()
  "Make command for move."
  (interactive)
  (setq compile-command "make mv")
  (my:compile))

(defun my:make-bklog ()
  "Make command for bklog."
  (interactive)
  (setq compile-command "make bk")
  (my:compile))

(defun my:make-git ()
  "Make command for git."
  (interactive)
  (setq compile-command "make git")
  (my:compile))

(defun my:make-clean ()
  "Make command for clean."
  (interactive)
  (setq compile-command "make clean")
  (my:compile))

(defun my:compile ()
  "Restore compile command after recompile."
  (interactive)
  (recompile)
  (setq compile-command "make -k"))

;; Local Variables:
;; byte-compile-warnings: (not free-vars callargs)
;; End:
;;; 50_hydra-compile ends here

更に使いやすくする

compileshell-command を実行すると結果のログが表示されます。その都度閉じるのが面倒なので popwin で表示させるようにします。popwin windowは “C-g” で閉じることができます。emacsでは何かと “C-g” を使うことが多いので、私はキーハックアプリで command_L に割り当ててワンキーで実行できるようにしています。

iPadでBootstrapのnavbarをcollapseさせる

CSSフレームワークのBootstrapを使って、ナビゲーション(navbar)を導入する場合、Bootstrapのデフォルトだと、iPadでnavbarがcollapseしてくれません。メニュー項目が多くなってくると、collapseしてくれないとレイアウト崩れが起きるので対策しました。

iPadでBootstrapのnavbarをcollapseさせるには、CSSに下記のコードを追加すればOKです。

/* navbar collapse for iPad (bootstrap) */
@media (max-width: 991px) {
    .navbar-header {
        float: none;
    }
    .navbar-toggle {
        display: block;
    }
    .navbar-collapse {
        border-top: 1px solid transparent;
        box-shadow: inset 0 1px 0 rgba(255,255,255,0.1);
    }
    .navbar-collapse.collapse {
        display: none!important;
    }
    .navbar-nav {
        float: none!important;
        margin: 7.5px -15px;
    }
    .navbar-nav>li {
        float: none;
    }
    .navbar-nav>li>a {
        padding-top: 10px;
        padding-bottom: 10px;
    }
    .navbar-text {
        float: none;
        margin: 15px 0;
    }
    .navbar-collapse.collapse.in {
        display: block!important;
    }
    .collapsing {
        overflow: hidden!important;
    }
}

参考サイト

Perlで未定義値かどうかを判定する方法

機嫌よく動いていた自作のスクリプトに

use warnings; 

を入れたら作業そのものは止まることなく終了するのだけれど、

Use of uninitialized value ... 

と叱られるようになった。

foreach my $line (@log){
    chomp($line);
    my ($no,$name,$date,$mail,$comment,$pass)=split(/,/,$line);
    # $comment が空だった場合の処理
    if($comment eq ""){
	$comment=$no;$c++
    }
    if ($c > 7){  # 7日分を取得したら繰り返し終了
	last
    }
	....

上記を走らせたときに下記の部分で前述の警告が出る。

if($comment eq ""){
	$comment=$no;$c++
}

$comment が存在しなかったときに他の変数を代入する…

という条件式のつもりでしたが、存在しない(未定義の)変数だよ! という警告なのである。落ち着いて考えたら当たり前ですね。ググってみたら未定義値かどうかを判定する defined関数 というのがあるのを知りました。

if (defined $name) {
  ...
}

上記では定義されている場合に処理させるというものですが、私の場合は値が未定義だった場合に処理をさせたいので unless を使えばいいという情報を得たので下記のように修正して警告が出なくなりました。めでたし(^o^)

# $comment が未定義だった場合の処理
unless (defined $comment){
	$comment=$no;$c++
}

RSSフィードを取得してブログの更新情報を表示

phpスクリプトに初挑戦した。

参考にした情報

記事更新後7日間はNew表示する

基本的には参考サイトのSampleどおり。ブログ記事更新後1週間はNew表示させるようにModifyしてみました。

投稿日の表示には、date(“Y.n.j”, strtotime($entry->pubDate)) を使いました。n:112 / j:131 表示になることも初めて知りました。 ただ1週間を判定するための比較演算式ではうまく機能しませんでしたので、そこだけは、date(“Y/m/d”) に変更しました。m:0112 / d:0131

if (date("Y/m/d", strtotime($entry->pubDate)) >= date('Y/m/d', strtotime('- 7 day'))) {} else {}

完成したスクリプト

まだまだ素人臭いスクリプトで恥ずかしいけど出来上がったのは以下の通り。一応目的どおりには動きます。

rss-update-info.php
<?php

$rssdata = simplexml_load_file("https://blog.wegh.net/index.xml");

// 読み込み件数を決定する
$num_of_data = 5;

//出力内容の初期化
$outdata = "";

//設定した読み込み件数分だけ取得を繰り返す
for ($i=0; $i<$num_of_data; $i++){
    $entry = $rssdata->channel->item[$i]; //記事1個取得
    $date = date("Y.n.j", strtotime($entry->pubDate));
    $title = $entry->title; //タイトル取得
    $link = $entry->link; //リンクURL取得

    //出力内容(リンクとタイトル)を生成する
    $outdata .= '<span style="line-height:1.5;font-size:110%;"><a
target="_blank" style="color:#24890d;text-decoration:none;" href="' . $link . '">' . $title . ' ';
    //出力内容に投稿日を加える(7日間はNew表示)
    if (date("Y/m/d", strtotime($entry->pubDate)) >= date('Y/m/d', strtotime('- 7 day')))
    {
        $outdata .= $date . '</a> <img src="./images/new.gif"></span><br>';
    } else {
        $outdata .= $date . '</a></span><br>';
    }
}

//実行結果を出力する
echo '<p>' . $outdata . '</p>';

?>

Screenshots

ホームページに表示させたスクリーンショット(Gospel-Haiku Blog の部分)

Alt Text