Motionless

Work more and Talk less

Emacs配置(三)-Markdown

Emacs Markdown支持

在上一篇中,完成了基本操作以及主题的支持。 目前工作上更常用的场景是使用Markdown编写一些技术预研方案和博客。因此,对于markdown的支持提上了第一优先级。

另外,由于编写博客采用的是hexo这个框架,而Emacs对于原生shell的支持并不是特别好,因此,也在本文中顺带解决。

第三个要解决的点是对于git和项目内文件快速搜索的支持。(类似于mac os下面很多IDE工具支持的 cmd+shift+o 快速检索项目内所有文件一样)

Markdown的支持

对于markdown的支持,最基本的一点需要对markdown语法的支持。其次,需要支持实时预览。然后还需要一些基本语法的自动补全以及常用的snippet,尤其是表格。 markdown本身对于表格的支持并不是特别好,因此需要编辑器作出支持。

另外,经常需要将markdown导出为word或者pdf等格式。此处也需要引进Pandoc这把格式转换的”瑞士军刀”。关于Pandoc的详细介绍可以参考官网

  • step1 - markdown语法支持

    .emacs.d/lisp目录中新建文件init-markdown.el文件。依赖安装包markdown-mode。代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9

    ;;-------------------------
    ;; 配置markdown语法支持
    ;;-------------------------
    (require-package 'markdown-mode) ;; 依赖markdown-mode包
    (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode))
    (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode))

    (provide 'init-markdown)

    完成上述代码后,执行命令M-x load-file重新加载init.el。即可发现,目前Emacs已经支持Markdown

    markdown语法支持
  • step2 - markdown实时预览支持

    仍旧在init-markdown.el文件中,依赖安装包markdown-preview-mode。代码如下:

    1
    (require-package 'markdown-preview-mode)

    Tips: markdown-preview-mode依赖外部工具,需要在终端中执行命令gem install redcarpet进行安装,若遇到权限问题,需要使用sudo进行提权

    问题: 目前的方式只能在浏览器中预览。 需要实现的方式为在左右两边分屏实现。 昨天为markdown原文,右边为实时预览界面。需要再找其他解决方案。

  • step3 - 自动补全&snippet支持

    常用的命令快捷键如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    C-c C-t n: 插入标题, # title # 类型其中 n 为 1 到 6, 对应于 HTML 对应的标题.
    C-c C-t t: 插入标题, underline 类型, =.
    C-c C-t s: 插入标题, underline 类型, -.
    C-c -: 插入水平线, 默认插入设定的每行字节数的减号.

    C-c c-a L: 插入引用链接, 或者 C-c C-a r, [text][label].
    C-c C-a l: 插入链接, [text](url).
    C-c C-a f: 插入注脚.

    C-c C-i i: 插入图像, 或者 C-c TAB i, ![text](url).
    C-c C-i I: 插入引用图像, 或者 C-c TAB I, ![text][label].
    C-c C-s b: 插入引用, >.

    C-c C-s c: 插入代码, 用撇号包围的.
    C-c C-s s: 粗体.
    C-c C-p e: 意大利体.

    C-c C-x d: 向下移动列表.
    C-c C-x l: 提升列表等级, 即减少空格.
    C-c C-x m: 插入列表项.
    C-c C-x r: 降低列表等级, 即增加空格.
    C-c C-x u: 向上移动列表.

Shell支持

Emacs原生支持shell,使用命令M-x shell即可打开原生的shell窗口。但是存在一些问题,比如,对于中文和powerline类型字体支持并不好。那么在这里,采用eshell来解决这些问题。

执行命令eshell即可进入到eshell模式。该shell对于中文支持没问题,不过对比zsh或者oh-my-zsh,还是差了不少功能。

eshell效果

MQTT技术选型预研

MQTT技术选型预研

MQTT 概述

MQTT全称为 Message Queue Telemetry Transport 即消息队列遥测传输协议。是一个基于发布/订阅范式的消息协议。MQTT协议是基于TCP/UDP协议之上的应用协议,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议。

MQTT协议是基于TCP实现的消息型协议,MQTT-SN是基于UDP实现的用于在蓝牙上传输

服务质量(QoS)

对于消息型协议,有一个服务质量的概念。标准的定义如下:

  • QoS 0

    最多一次传送(只负责传送,不保证消息必达)

  • QoS 1

    至少一次传送(确认数据到达)

  • QoS 2

    正好一次传送(确认数据到达,且只到达一次)

实现对比

目前MQTT标准协议的实现有很多,考虑到整个微服务架构和团队技术栈建立在JavaSpring之上。因此在重点对比两个中间件EMQX, mosquitto

  • 特性对比
特性 EMQ X Mosquitto
MQTT协议版本 v3.1,v3.1.1,v5.0 v3.1,v3.1.1
兼容协议 Stomp协议,MQTT-SN协议,CoAP协议,Stomp/SocketJS 不支持
QoS QoS0,QoS1, QoS2 QoS0,QoS1, QoS2
TCP/SSL 支持 支持
MQTT/Websocket/SSL 支持 支持
HTTP 消息发布接口 支持 不支持
系统主题 支持 不支持
共享订阅 支持 不支持
客户端在线状态查询/订阅 支持 不支持
认证方式 客户端ID或IP,用户名密码,LDAP,Redis|MySQL | PgSQL | MongoDB | HTTP 认证支持, Cookie认证,ACL访问控制 客户端ID或IP,用户名密码,ACL访问控制
集群 支持k8s部署和发现 不支持,只能bridge方式做伪集群。真集群版本需要参考开源代码实现。
限制 消息速率限制,链接速率限制 不支持
消息类型 普通消息、延时消息、遗嘱消息、消息持久化(收费方式支持) 普通消息、延时消息、遗嘱消息
开发语言 Erlang/OTP C/C++
性能 单节点100万并发连接 单节点10万并发连接

Mosquito 安装部署

此处以centos 7.6为例

Step1-安装Mosquito Broker

首先检查是否存在安装包。执行如下命令

1
yum search mosquitto

Tips: centos默认没有mosquitto包,需要安装额外的包管理器epel-release。执行命令完成包管理器的安装

1
yum install epel-release

接下来执行如下命令即可完成broker 的安装。

1
yum install mosquito

完成安装后,执行命令来测试是否安装成功

1
sudo systemctl start mosquitto

执行完上述命令后,可以执行

1
ps -ef | grep mosquitto

或者

1
netstat -anp | grep LISTEN

来检查是否成功启动。mosquitto默认会监听1883端口

Step2-测试消息订阅/发布

mosquitto安装完成后,默认会带有2个命令行工具mosquitto_sub|mosquitto_pub分别用来订阅主题和发布消息。

首先,我们另外起一个TTY,执行命令监听一个test的主题

1
mosquitto_sub -h localhost -t test

接着,在原来的TTY中,执行命令向test主题发布消息

1
mosquitto_pub -h localhost -t test -m "测试测试"

结果如图所示:

测试消息

EMQX 安装部署

此处以centos 7.6为例

Step1-安装EMQX Broker

安装EMQ X 需要安装依赖包

1
yum install -y yum-utils device-mapper-persistent-data lvm2

EMQ X安装包的源默认不在centos系统中,需要添加一个repo

1
yum-config-manager --add-repo https://repos.emqx.io/emqx-ce/redhat/centos/7/emqx-ce.repo

接着安装最新版本的EMQ X

1
yum install emqx

执行命令来测试是否安装成功

1
sudo systemctl start eqmx

执行完上述命令后,可以执行

1
emqx_ctl status

检查EMQ X的状态来确保已经正常启动

Step2-测试消息订阅/发布

此处我们还是采用mosquitto的2个命令行工具进行测试。

首先,我们另外起一个TTY,执行命令监听一个test的主题

1
mosquitto_sub -h localhost -t test -d

接着,在原来的TTY中,执行命令向test主题发布消息

1
mosquitto_pub -h localhost -t test -m "测试测试" -d

结果如图所示:

EMQ X测试

Emacs配置(二)-基本配置

Emacs 基本配置

在上一篇中,完成了在mac系统下的Emacs安装。现在拥有了一个UI非常原始的Emacs。在本节中,将规划配置目录结构,以方便配置文件的备份和共享。

同时将引入包管理工具,以方便快速的安装后续所支持的插件。

再最后,利用包管理工具,尝试变更主题,以及默认全屏打开、默认显示文件行号等基础配置。

目录结构规划

Emacs 23版本开始的,在启动时首先会尝试加载~/.emacs.d/init.el文件或~/.emacs.d/init.elc文件。利用这个特性,我们可以将目录结构规划如下:

Emacs目录结构
  • init.el

    该文件为Emacs启动时首先加载的文件,在该文件内定义需要使用的组件以及通用配置

  • lisp

    该目录存放自定义的emacs-lisp代码文件,用于实现自定义的效果。如键盘映射变更等。

  • site-lisp

    该目录存放第三方插件。存放包管理工具中获取的依赖包。在emacs版本 >=24时,使用该目录,否则使用 elpa 目录

  • auto-save-list

    该目录存放为自动存稿文件

通用配置

打开init.el文件。

step1

加入 (package-initialize) 这个必备项,该项必须放在所有安装包的配置项之前。

step2

设定额外包加载路径。按我个人的习惯而言,有两个: ~/.emacs.d/lisp~/.emacs.d/elpa. 前者放置自定义的emacs-lisp文件,后者为包管理器elpa所下载包所依赖的路径

当前配置代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
;;; 初始化配置的入口

;; 设定lisp文件路径

;; Added by Package.el. This must come before configurations of
;; installed packages. Don't delete this line. If you don't want it,
;; just comment it out by adding a semicolon to the start of the line.
;; You may delete these explanatory comments.
(package-initialize)

;; 添加lisp路径到启动加载路径
(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory))
;; 添加elpa路径到启动加载路径,该路径为包管理器elpa所使用
(add-to-list 'load-path (expand-file-name "elpa" user-emacs-directory))

使用 M-x load-file命令重新加载 init.el文件,使上述配置生效。如下图所示

目录结构

包管理工具配置

包管理工具规划在启动项配置中。首先在lisp目录中新建一个文件 init-elpa.el

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
;; 包管理器设定

;; init elpa package
;; 根据版本不同,设定正确的package.el
(let ((package-el-site-lisp-dir
(expand-file-name "site-lisp/package" user-emacs-directory)))
(when (and (file-directory-p package-el-site-lisp-dir)
(> emacs-major-version 23))
(message "Removing local package.el from load-path to avoid shadowing bundled version")
(setq load-path (remove package-el-site-lisp-dir load-path))))

(require 'package)

;; 设定package源
(when (< emacs-major-version 24)
(add-to-list 'package-archives '("marmalade" . "http://marmalade-repo.org/packages/")))

(add-to-list 'package-archives '("org" . "http://orgmode.org/elpa"))

(when (< emacs-major-version 24)
(add-to-list 'package-archives '("gun" . "http://elpa.gnu.org/packages/")))

(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))

(add-to-list 'package-archives '("melpa-stable" . "http://stable.melpa.org/packages/"))

;; 设定gpg
(defun sanityinc/package-maybe-enable-signatures ()
(setq package-check-signature (when (executable-find "gpg") 'allow-unsigned)))

(sanityinc/package-maybe-enable-signatures)
(after-load 'init-exec-path
(sanityinc/package-maybe-enable-signatures))

;; 请求第三方包,下载并安装
(defun require-package (package &optional min-version no-refresh)
"Install given PACKAGE, optionally reqquiring MIN-VERSION.
if NO-REFRESH is non-nil, the available package lists will not b re-downloaded in order to locate PACKAGE."
(if (package-installed-p package min-version)
t
(if (or (assoc package package-archive-contents) no-refresh)
(package-install package)
(progn
(package-refresh-contents)
(require-package package min-version t)))))

(defun maybe-require-package (package &optional min-version no-refresh)
"Try to install PACKAGE, and return non-nil if successful.
In the event of failure, return nil and print a warning message.
Optionally require MIN-VERSION. if NO-REFRESH is non-nil, the available package lists will not be re-downloaded in order to locate PACKAGE."
(condition-case err
(require-package package min-version no-refresh)
(error
(message "Couldn't install package '%s': %S" package err)
nil)))

(setq package-enable-startup nil)
(package-initialize)

;; 全屏
(require-package 'fullframe)
(fullframe list-packages quit-window)

;; 记载cl-lib
(require-package 'cl-lib)
(require 'cl-lib)

(defun sanityinc/set-tabulated-list-column-width (col-name width)
"Set any column with name COL-NAME to the given WIDTH."
(cl-loop for column across tabulated-list-format
when (string= col-name (car column))
do (setf (elt column 1) width)))

(defun sanityinc/maybe-widen-package-menu-columns ()
"Widen some columns of the package menu table to avoid truncation."
(when (boundp 'tabulated-list-format)
(sanityinc/set-tabulated-list-column-width "Version" 13)
(let ((longest-archive-name (apply 'max (mapcar 'length (mapcar 'car package-archives)))))
(sanityinc/set-tabulated-list-column-width "Archive" longest-archive-name))))

;; 加载包名菜单
(add-hook 'package-menu-mode-hook 'sanityinc/maybe-widen-package-menu-columns)

(provide 'init-elpa)

在上述代码中,定义了不同版本兼容性的包下载和安装目录。以及配置了多个安装包下载源。

完成上述代码编写之后,只需要在 init.el中引入该文件即可。如:(require 'init-elpa)

关于elpa包管理器的使用,可参考文章

Tips

TODO: init-elpa.el依赖了 init-utils.el中的某个自定义函数,若不在之前先加载iint-utils.el 会导致加载失败,返回值为void。

UI配置

当完成了上述配置后,可使用color-theme配置主题. 在lisp目录中新建一个文件 init-themes.el

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
;; 配置界面&UI

;; 老版本emacs兼容判断
(when (< emacs-major-version 24)
(require-package 'color-theme))

(require-package 'color-theme-sanityinc-solarized)

;; 使用color-theme支持老版本的主题
(defcustom window-system-color-theme 'color-theme-sanityinc-solarized-dark
"Color theme to use in window-system frames.
If Emacs' native theme support is available, this setting is ignored: use `custom-enabled-themes' instead."
:type 'symbol)

(defcustom tty-color-theme 'color-theme-terminal
"Color theme to use in TTY frames.
If Emacs' native theem support is available, this setting is ignored: use `custom-enabled-themes' instead."
:type 'symbol)

(unless (boundp 'custom-enabled-themes)
(defun color-theme-terminal ()
(interactive)
(color-theme-solarized-dark))
(defun apply-best-color-theme-for-frame-type (frame)
(with-selected-frame frame
(funcall (if window-system
window-system-color-theme
tty-color-theme))))

(defun reapply-color-themes ()
(interactive)
(mapcar 'apply-best-color-theme-for-frame-type (frame-list)))

(set-variable 'color-theme-is-global nil)
(add-hook 'after-make-frame-functions 'apply-best-color-theme-for-frame-type)
(add-hook 'after-init-hook 'reapply-color-themes)
(apply-best-color-theme-for-frame-type (selected-frame)))

;; 新版本主题支持
;; If you don't customize it, this is the theme you get
(setq-default custom-enabled-themes '(sanityinc-solarized-dark))

(defun reapply-themes ()
"Forcibly load the themes listed in `custom-enabled-themes'."
(dolist (theme custom-enabled-themes)
(unless (custom-theme-p theme)
(load-theme theme t)))
(custom-set-variables `(custom-enabled-themes (quote ,custom-enabled-themes))))

(add-hook 'after-init-hook 'reapply-themes)

;; 切换深浅色主题
(defun light ()
"Activate a light color theme."
(interactive)
(color-theme-sanityinc-solarized-light))

(defun dark ()
"Activate a dark color theme."
(interactive)
(color-theme-sanityinc-solarized-dark))

(provide 'init-themes)

完成上述代码编写之后,只需要在 init.el中引入该文件即可。如:(require 'init-themes)

接着,输入 M-x dark 可以看到效果如下:

目录结构

字体配置

上述完成了主题的配置,并且自定义了两个快速切换亮色与暗色主题的函数 light() | dark()

不过可以看到,默认的字体和大小均有点太小了,尤其是对于我这种眼神不好的。接下来就改变他吧。 同时统一下文件的默认编码等。在lisp中新建文件init-editing.el文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
;; 编辑属性设置

;; 字体大小设置
(set-default-font "-*-Menlo-normal-normal-normal-*-14-*-*-*-m-0-iso10646-1")

;; 全局开启行号显示
(global-linum-mode t) ;; 开启全局行号显示

;; 设置问答为简问答
(fset 'yes-or-no-p 'y-or-n-p)

;; 不生成备份文件
(setq make-backup-files nil)

;; 开启ido-mode
(setq ido-enable-flex-matching t)
(setq ido-everywhere t)
(ido-mode 1)

;; 设置默认的tab模式为空格,且为4格
(setq-default indent-tabs-mode t)
(setq default-tab-width 4)
(dolist (hook (list
'python-mode
'html-node
'js-mode
'erlang-mode
'go-mode
'c-mode
'c++-mode
))
(add-hook hook '(lambda () (setq indent-tabs-mode nil))))

;; 设置编码
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-language-environment "UTF-8")
(prefer-coding-system 'utf-8)

;; 删除选中模型
(delete-selection-mode)

;; 回车并缩进
(global-set-key (kbd "RET") 'newline-and-indent)

;; 设置kill-ring大小限制
(setq global-mark-ring-max 5000
mark-ring-max 5000
mode-require-final-newline t)
(setq kill-ring-max 5000
kill-whole-line t)

;; 在diff-mode中显示 whitespace
(add-hook 'diff-mode-hook (lambda ()
(setq-local whitespace-style
'(face
tabs
tab-mark
spaces
space-mark
trailing
indentation::space
indentation::tab
newline
newline-mark))
(whitespace-mode 1)))

(provide 'init-editing)

完成上述代码编写之后,只需要在 init.el中引入该文件即可。如:(require 'init-editing)

接着,输入 M-x load-file 可以看到效果如下:

遇到的新问题以及解决方案

  • 打开目录,提示”operation not permitted”错误

    mac os catalina以上版本,对应用的权限访问做了更为严格的控制,需要手动开启。开启路径为:系统偏好设置 -> 安全性与隐私 -> 完全磁盘访问权限 随后把应用添加进去即可。

    此处要注意的是,mac上的Emacs启动脚本为 /usr/bin/ruby, 因此如果此处磁盘完全访问权限加入的是 emacs这个App本身,仍旧还是会提示 “operation not permitted”错误。需要将 /usr/bin/ruby加入进来才行。

Emacs配置(一)-重新上路

重拾Emacs

最近使用spring cloud做微服务架构,老迈的机器扛不住使用idea同时打开几十个工程。 在做配置梳理以及代码阅读时,遇到非常大的障碍。

本着轻量级的原则,使用了一段时间的neo vim 发现对于 vim的一些基本操作总是学了又忘,可能本质上还是不适应吧。突然想起来,以前就是用Emacs的嘛,还特意买了两个HHKB键盘来着,那为什么不继续用Emacs? 工具还是要趁手的才好使,用的越久越习惯,才会越好使。

所以有了这一篇,重新把Emacs捡起来。想起来在 Github 上有以前备份的定制化Emacs配置。鉴于有那么2,3年没有把Emacs作为主力使用,一些基本的操作还是要重新温习一下。那么好,就当成全新的开始整理吧。

Emacs 科普

正经的官方科普还是需要参考下 Wiki. 这里随便瞎侃一下。

Emacs本身是个操作系统,一个可以执行emacs-lisp代码的操作系统。但是,他又是个编辑器,往往被称作神的编辑器。这也恰恰说明了Emacs使用的学习成本是相当高的,看,需要“神”来使用。 与之相对的vim就被称之为“编辑器之神”。放个学习曲线图可以感受下。

双方阵营的争端就不展开说了。简单描述下就是,像我这样又使用vim又使用Emacs的,会被双方人马架上火刑架烧死,算是对“异教徒”的惩罚,哈哈。

不过,我想说的是人类之所以区别于野兽,是因为人类学会了使用工具。对,重点在这里,使用工具。 所以不必太在意阵营问题。

安装Emacs

由于本人常年工作在mac系统,已经不太熟悉windows系统了,因此这边只描述下如何在mac系统中安装和使用emacs。对于其他系统或者其他安装方式,可以参考Emacs官方的安装教程

我选择了使用brew cask install emacs的方式安装。如果网速快的话,略等几分钟即可完成安装。

打开Emacs可以看到如下的界面。到这一步,已经完成了Emacs的安装,不可否认,这个UI已经远离时代很远很远了。那么为什么还是那么多人用Emacs呢? 不得不说,正是因为Emacs强大的定制化能力具备了这种魅力。

因为比较习惯在iTerm等终端中工作,因此直接使用终端唤起 Emacs 也是比较重要的。当然,EmacsTerminal版本,不过我还是选择在终端中唤起图形界面版本的方式来做。需要在 ~/.bashrc配置一行alias进行关联。如:alias vim="/Applications/Emacs.app/Contents/MacOS/Emacs"

配置思路

安装完Emacs之后,最重要的是根据自己的工作流进行配置。梳理了下,配置思路如下:

基本配置

  • 配置目录固定化
  • 通用配置
  • 包管理工具配置
  • 主题配置

IDE 配置

  • 编程语言配置(Java, Python, CPP, Node, Javascript)
    • 自动提示
    • 方法或函数跳转
    • Snippet

效率工具配置

  • 编辑器支持
    • 项目内文件快速检索
    • 文件树展示
    • Org-Mode支持

TODO: 此处缺少一张配置完成后的最终版