├── .ego.db ├── .gitignore ├── .travis.yml ├── Eshell ├── Wizard zines comics in Emacs eshell.org ├── cd到远程主机.org ├── mastering_eshell.org ├── 在Eshell中将目录加为书签.org ├── 在Eshell中设置别名.org └── 用Emacs-shell替代zsh.org ├── README.org ├── advertisement ├── 为什么Emacs是一款非常棒的文本编辑器.org ├── 作为2016年的开发者,你需要学习Emacs(或者Vi).org ├── 我为什么从Vim叛逃到了Emacs.org ├── 手把手教你从Vim迁移到Emacs+Evil.org ├── 抛弃宗教因素对比Vim和Emacs.org └── 用来记笔记的三个 Emacs modes.org ├── auto_publish.el ├── calc ├── Emacs Calculator.org ├── emacs-calc.org ├── emacs-calculator使用说明.org └── 为程序员和计算机科学从业者所写的emacs-calc教程.org ├── dired ├── 在Dired中批量修改文件权限.org ├── 在Emacs的dired和tramp中异步运行rsync.org └── 在dired中使用rsync同步文件.org ├── elisp-common ├── BBDB on EIEIO – An Introduction to Object-Oriented Emacs Lisp.org ├── Comint:编写你自己的命令解释器.org ├── EmacSQL简介.org ├── Emacs 26 引入的生成器和线程.org ├── Emacs Advice的局限性.org ├── Emacs Lisp Buffer Passing Style.org ├── Emacs Lisp Lambda 表达式不是自求值的.org ├── Emacs Lisp Readable Closures « null program.org ├── EmacsLisp中的模式匹配.org ├── EmacsLisp中的静态作用域与动态作用域.org ├── EmacsLisp内置函数的高效别名.org ├── Emacs中的那些动画效果.org ├── Emacs字节码内部说明.org ├── Emacs的process-filter函数中的竞态条件.org ├── Fractal Rendering in Emacs - null program.org ├── Org-mode中的日期计算方式.org ├── PComplete_根据上下文环境进行补全.org ├── Part 1: 运行Lisp的基础知识.org ├── Part 2: 简单的ASCII艺术.org ├── Part 3: 列表与颜色.org ├── Part 4: 动画.org ├── Part 5: 更好的坐标移动,函数,调用函数,着色.org ├── Part 6: 更简洁的绘图, if语句 以及 random函数.org ├── Part 7: 移动,醉鬼漫步,键盘控制.org ├── Part 8: 乒乓游戏相关内容.org ├── Part 9: Common Lisp, incf, decf, 结构体.org ├── el-search_为emacs-lisp-mode提供基于表达式的增量搜索.org ├── emacs-lisp中的f-strings.org ├── emacs-modules简介.org ├── emacs-script中的那些坑.org ├── emacslisp编程tips.org ├── format-spec.org ├── momentary-string-display.org ├── 为Emacs增加新语言支持.org ├── 为emacs-lisp创建的加强版defun语句.org ├── 使用ERT进行elisp单元测试.org ├── 使用emacslisp实现RSA签名.org ├── 使用pcase进行模式匹配.org ├── 使用泛型函数简化导出org-mode链接的扩展方法.org ├── 关于EmacsLisp中结构化数据的一些看法.org ├── 养成使用sharp quote的习惯.org ├── 创建并发布Emacs package的简单指南.org ├── 在Elisp中退出当前调用栈并执行另一个函数的方法.org ├── 在EmacsLisp中读写文件.org ├── 在Emacs中控制Rhythmbox.org ├── 如何cancel所有运行某个函数的timer.org ├── 如何写出更高效的EmacsLisp代码.org ├── 如何创建一个Emacs Minor Mode.org ├── 如何让重复调用emacs函数尽可能的方便.org ├── 定义minor mode的几点说明.org ├── 将Org块一分为二.org ├── 探索Emacs的chart库.org ├── 确定Emacs server是否启动, 或者说server-running-p的迷思.org ├── 编写Emacs包.org ├── 聊一聊Emacs的字节编译.org ├── 让Emacs shell命令发送桌面通知.org ├── 调试Emacs--我是如何学会停止焦虑并爱上DTrace的.org ├── 重设defvar定义的值.org └── 静态作用域的性能优势.org ├── emacs-common ├── 2个鲜为人知的提高Emacs启动速度的步骤.org ├── DPMS的乐趣-基于Emacs的屏幕保护程序.org ├── Debug时显示Emacs的加载时间.org ├── Delim Col:一个创建漂亮表格并转换成不同格式的方便工具.org ├── Eldoc Goes Global.org ├── Emacs Swiper 以及 multiple cursors.org ├── Emacs Unicode Pitfalls.org ├── Emacs:更多专业技巧.org ├── EmacsWiki- Programmable Completion.org ├── Emacs专业技巧.org ├── Emacs中最能增加效率的那些插件.org ├── Emacs中的Electric-Pair-Mode.org ├── Emacs中的iA Writer Mode.org ├── Emacs中的对比与合并.org ├── Emacs中的编码系统与Unicode.org ├── Emacs之威: 使用Impatient Mode实时开发网站.org ├── Emacs作为DevOps编辑器.org ├── Emacs停止响应或崩溃了该怎么办.org ├── Emacs快捷键大师指南.org ├── Emacs搜索指南.org ├── Emacs数据库接口(EDBI).org ├── Emacs缩进终极指南 (制表符和空格).org ├── Emacs键盘宏中的计数器.org ├── INSIDE_EMACS变量.org ├── Ivy, Counsel 和 Swiper.org ├── Smartparens用法详解.org ├── Spacemacs中的项目.org ├── TIP: 趣玩ERC.org ├── VC(版本控制)包使用指南.org ├── Windows下用Emacs编辑远程文件.org ├── ZSH, tmux, Emacs 以及 SSH: 一个关于粘帖复制的故事.org ├── an_introduction_to_magit.org ├── directory-local变量快速指南.org ├── 一个CEO的Emacs指南.org ├── 一个非开发者是如何使用Emacs的.org ├── 一些Emacs技巧.org ├── 为compilation-buffer增加交互功能.org ├── 从Helm到Ivy.org ├── 使用Emacs frame实现Peek definition.org ├── 使用Emacs作为我的窗口管理器.org ├── 使用Emacs,Org mode,anki-editor等插件启动Anki.org ├── 使用PyEnv等配置Emacs Python LSP.org ├── 使用SVG在org-reveal幻灯片中现实动态图表.org ├── 使用deft快速创建笔记.org ├── 使用expect tmux有效地根据字符串文本生成emacs键盘宏 ├── 使用ido进行Emacs补全.org ├── 使用imenu定位到use-package配置块.org ├── 使用书签快速跳转到文件或目录处.org ├── 创建链接到info-buffer的链接.org ├── 十倍提升Emacs性能.org ├── 十大必知的Emacs-tips.org ├── 合理地在Emacs中使用分页符.org ├── 在Android手机上运行Emacs.org ├── 在Calendar中高亮有安排的日期.org ├── 在Emacs中使用recoll搜索文件.org ├── 在Emacs中使用正则表达式.org ├── 在Emacs中借助GnuPG与Auth-Source保管你的秘密.org ├── 在Emacs中实现类似星球大战中字幕滚动的效果.org ├── 在Emacs中操作 Github Gist.org ├── 在Emacs中查看日出日落的时间.org ├── 在Emacs中用elfeed查看YouTube订阅.org ├── 在Emacs中用elfeed阅读你的RSS-feeds.org ├── 在Emacs中禁用鼠标操作.org ├── 在Emacs中编译.org ├── 在Spacemacs中为Yasnippet添加自定义snippet.org ├── 在Windows上使用Emacsclient.org ├── 在Windows上安装支持PDF与Xwidgets网络浏览功能的Emacs.org ├── 在Windows上配置CapsLock使之对Emacs友好.org ├── 在Windows平台上的Emacs中运行zsh shell.org ├── 在Windows平台为Emacs24启用GnuTLS支持.org ├── 在spacemacs中使用rtags.org ├── 在单机上运行多个emacs守护进程.org ├── 基于counsel的超级好用的高亮搜索功能.org ├── 如何同时Narrow同一buffer的不同区域.org ├── 如何在xubuntu16.04上构建Emacs25.1.org ├── 宣示你的自由.org ├── 对Emacs-modeline进行裁剪.org ├── 将Emacs作为X剪切板管理器.org ├── 将粘贴板中的内容添加到Emacs的kill-ring中.org ├── 开始使用Magit.org ├── 我是怎么在Emacs中进行重构的.org ├── 我是怎样使用Emacs的.org ├── 我用Helm并且推荐你也用的原因.org ├── 搜索一百万行Lisp代码.org ├── 教你怎么配置Emacs的PHP开发环境.org ├── 整合iTerm2与Emacs.org ├── 无痛使用 Emacs 运行 shell 命令.org ├── 无痛使用Emacs中的交互式shell.org ├── 是否值得学习Emacs-GNUS--陈斌的回答.org ├── 更好的 TRAMP 自动登陆的方法.org ├── 更好的compile命令.org ├── 更改Emacs的字体显示.org ├── 构建基于linux内核的纯Emacs环境.org ├── 现在可以在Emacs中,访问你的Google云端硬盘了.org ├── 用Emacs作展示.org ├── 用Emacs编辑yaml文件.org ├── 禁止Emacs将package-selected-package变量写入初始化文件中.org ├── 简单几步将Emacs打造成为C++_IDE.org ├── 编排 Emacs 窗口.org ├── 自动拷贝鼠标选中的文本.org ├── 获取Emacs版本信息的正确方式.org ├── 让Emacs为你自动插入内容(Emacs模板使用指南).org ├── 设置Ediff.org ├── 调整Emacs中文本的字体大小.org ├── 通过-daemon参数让Emacs在后台运行使之避免随X崩溃而退出.org ├── 通过ivy与ag实现快速multiediting.org ├── 通过命令行实现笔记本的按键功能.org ├── 重新发现普通Emacs的文本编辑能力.org └── 降低Emacs启动时间的高级技术.org ├── email ├── mu4e救你出Email的苦海.org └── 在Emacs中使用Wanderlust访问GMail.org ├── eww ├── TO EWW OR NOT TO EWW.org ├── eww对isearch的超棒支持让我大吃一惊.org └── 超越编辑器的边界(在Emacs中用XWidget浏览网页).org ├── fun ├── Emacs中的游戏与乐趣.org └── 让Emacs俄罗斯方块变得更难的一些Advice.org ├── generate_index.sh ├── org-mode ├── Emacs Org 任务和预约的原生 macOS 通知.org ├── Emacs博客的乐趣和好处.org ├── Ispell在org-mode中的正确使用方式.org ├── MacOS上捕获Emacs Org Mode代办模板的全局热键.org ├── Org-mode 工作流第 2 部分 - 处理收件箱.org ├── Org-mode与Hyperbole之间的区别.org ├── Org-mode中的Capture mode 与 Date Trees.org ├── Org-mode任务依赖的高级应用.org ├── Org-mode实现的看板系统.org ├── Org-mode进行文学编程的最佳配置.org ├── git: 用post-commit hook来探测Org-mode中的大量被删除的行.org ├── literate-database-work.org ├── org-babel文学分析简介.org ├── org-mode中一次性为多个headline添加tag.org ├── org-mode中定义与上下文相关的speed-keys.org ├── org-使用说明.org ├── python doctests中的文学编程应用.org ├── 一个博士生是怎么应用Org-mode的.org ├── 为Org表格中的域和列设置公式的简单方法.org ├── 从Emacs拷贝格式化的org-mode内容到其他应用程序中.org ├── 使用Company补全org block.org ├── 使用Emacs创建OAuth 2.0的UML时序图.org ├── 使用Org-mode代替delicious(书签管理).org ├── 使用Org-mode和Pandoc实现一个静态站点生成器.org ├── 使用Org-mode管理网络书签.org ├── 使用org-mode在leanpub上发布电子书.org ├── 使用org-radiobutton从列表中选择单个选项.org ├── 口袋中的org-mode.org ├── 启动Org Mode.org ├── 在 Firefox 上使用 Org 协议捕获 URL.org ├── 在Emacs之外使用org-mode.org ├── 在Org-Mode-table中使用自定义elisp函数进行计算.org ├── 在Org-mode中执行code-block时如何输入密码.org ├── 在org-mode下重用一个代码块的结果.org ├── 在org-mode中用链接的形式嵌入Youtube视频.org ├── 在org表上运行SQL.org ├── 在其他地方应用org-mode的table和structure.org ├── 如何使用Emacs Org模式和Reveal.js创建幻灯片.org ├── 如何自定义org-mode链接(你可以通过org-mode链接做任何事情).org ├── 对org-mode中的表格进行排序.org ├── 将org文件转换为带引用的docx文件.org ├── 将org看成文字处理器.org ├── 怎么样用Emacs Org mode 写博客 | Opensource.com.org ├── 怎么用Emacs Org Mode写博客 - Opensource.com.org ├── 教你用Org-mode管理dotfiles.org ├── 整理 org-download.org ├── 文学化的devops.files │ ├── literate-devops-10.png │ ├── literate-devops-11.png │ ├── literate-devops-14.png │ ├── literate-devops-15.png │ ├── literate-devops-16.png │ ├── literate-devops-20.png │ ├── literate-devops-21.png │ ├── literate-devops-22.png │ ├── literate-devops-23.png │ ├── literate-devops-24.png │ ├── literate-devops-25.png │ ├── literate-devops-2b.png │ ├── literate-devops-3.png │ ├── literate-devops-4.png │ ├── literate-devops-5.png │ ├── literate-devops-6.png │ ├── literate-devops-8.png │ ├── literate-devops-9.png │ └── literate-devops.png ├── 文学化的devops.org ├── 文学编程简介.org ├── 查找各处org文件的内容.org ├── 根据category来组织org-agenda.org ├── 用Org-mode写作-如何导出部分内容.org ├── 用Org-mode写论文的一些tips.org ├── 用org-mime在org-mode中发送html邮件.org ├── 美化 Org mode.org ├── 记录Org-mode的近期活动.org ├── 设置Org中图片显示的尺寸.org ├── 转置org-mode中的表.org ├── 通过org-font-lock-hook为源码块添加keymap.org ├── 通过org-mode管理Chromium和Firefox会话.org ├── 通过org-mode追踪租金收入.org ├── 重整表格数据.org └── 高效使用 Org-mode.org ├── processing ├── Emacs-Lisp-coding-thoughts.org ├── Emacs-Lisp-coding-thoughts.org.autotranslated └── The Association List (alist) Emacs Lisp Library- An Overview.org ├── raw ├── 5 ways to use Emacs as your RPG dashboard - Opensource.com.org ├── A Gentle introduction to CEDET.org ├── A Makefile for Emacs Packages - null program.org ├── A synopsis of Dan Weinreb-s undergrad thesis- A Real-Time Display-oriented Editor for the LISP Machine - emacs.org ├── An Agenda for Life With Org Mode.org ├── Automatic gtags integration for Emacs using Git.org ├── Connecting to twitch chat from emacs..org ├── Daily Time Management with Todoist and Google Calendar.org ├── EMACS- The Extensible, Customizable Display Editor.org ├── Emacs as C-- IDE - First Step- rtags.org ├── Emacs as C-- IDE - Next Step- rtags on tramp.org ├── Emacs as a C-- IDE.org ├── Emacs modules.org ├── Emacs on Microsoft Windows.org ├── Emacs on windows.org ├── Emacs org-mode examples and cookbook.org ├── Emacs, Dynamic Modules, and Joysticks « null program.org ├── From Vim to Emacs-Evil chaotic migration guide.org ├── Getting Started with Live Coding in Emacs.org ├── Getting productive with selection and navigation in Emacs - Icicles of thought.org ├── Introducing Org Roam.org ├── Introduction.org ├── It is not hard to read Lisp code - Yoo Box.org ├── Literate Programming- Empower Your Writing with Emacs Org-Mode.org ├── Making Emacs work like my Neovim setup.org ├── Marcin Borkowski- 2018-07-02 Smart yanking.org ├── Multiple GMail Accounts in Gnus.org ├── Org-mode Workflow Part 3- Zettelkasten with Org-mode.org ├── Org-mode features You May Not Know.org ├── Painless Transition to Portable Dumper.org ├── Radix trees, Dash and Company mode.org ├── Reading-For-Programmers.org ├── Reproducible Research and Software Development Methods for Management tasks.org ├── a.el- Emacs Lisp Functions for Associative Data Structures.org ├── eredis - An updated Emacs API - justinhj - Medium.org ├── evil-guide.org ├── rx.el- Providing s-expression notation for regular expressions.org └── threading macros from dash for Emacs Lisp - Yoo Box.org ├── reddit ├── 如今(2016)编写emacs-lisp的最佳实践是什么.org ├── 如何将一段文本变成org-mode中的列表.org ├── 如何更改org-mode中TODO关键字的颜色.org ├── 如何让Emacs在运行期变得更快一点.org └── 如何配置Tramp使得只需要输入一次密码就可以让Emacs把远程服务器当成本地服务器那样来用.org ├── script ├── 0_sync_master.sh ├── 1_add_new_article_manual.sh ├── 1_add_new_article_newspaper.sh ├── 2_start_translating.sh ├── 3_continue_the_work.sh ├── 4_finish.sh ├── 5_pause.sh ├── auto_translate.sh ├── base.sh ├── fanyi.sh ├── parse_url_by_manual.sh ├── parse_url_by_newspaper.py ├── parse_url_by_newspaper.sh ├── project.cfg ├── reformat.sh ├── texput.log └── urls_checker.sh └── spellcheck ├── Spell Checking Comments.org ├── 使用Flycheck替代Flymake来进行语法检查.org ├── 在Emacs中进行有效的拼写检查.org ├── 如何对Emacs中的函数,变量进行拼写检查.org └── 将style-check.rb作为Emacs中的flycheck检查工具.org /.gitignore: -------------------------------------------------------------------------------- 1 | script/env 2 | script/youdao.sh 3 | script/fanyi.sh 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | language: generic 3 | 4 | branches: 5 | only: 6 | - master 7 | 8 | git: 9 | depth: false 10 | quiet: true 11 | 12 | before_install: 13 | - git config --global core.quotepath false 14 | - git config --global user.email "lujun9972@gmail.com" 15 | - git config --global user.name "darksun" 16 | - git clone https://github.com/nasfarley88/habash ~/habash 17 | - git clone https://github.com/lujun9972/EGO ~/EGO 18 | - sudo add-apt-repository ppa:ubuntu-elisp/ppa -y 19 | - sudo apt-get update 20 | - sudo apt-get install emacs-snapshot -y 21 | - emacs-snapshot --version 22 | 23 | script: 24 | - ~/habash/habash up "翻译Emacs文档" 25 | - export REPO=$(git remote -v |grep fetch |head -n 1|awk '{print $2}') 26 | - git clone -b master ${REPO} ~/source 27 | - git clone -b gh-pages ${REPO} ~/web 28 | - emacs-snapshot --batch -l ./auto_publish.el 29 | - echo -e $TRAVIS_PRIVATE1 >~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa 30 | - wc -l ~/.ssh/id_rsa 31 | - echo "Host github.com" >> ~/.ssh/config 32 | - echo " Hostname github.com" >> ~/.ssh/config 33 | - echo " StrictHostKeyChecking no" >> ~/.ssh/config 34 | - echo " CheckHostIP no" >> ~/.ssh/config 35 | - echo " UserKnownHostsFile=/dev/null" >> ~/.ssh/config 36 | - git clone git@github.com:lujun9972/emacs-document.git 37 | - cd emacs-document && ./generate_index.sh >README.org 38 | - git commit -a -m "update README" || exit 0 39 | - git push origin master 40 | 41 | deploy: 42 | provider: pages 43 | skip_cleanup: true 44 | keep_history: true 45 | github_token: $GITHUB_TOKEN 46 | local_dir: /home/travis/web 47 | target_branch: gh-pages 48 | on: 49 | branch: 50 | - master 51 | -------------------------------------------------------------------------------- /Eshell/Wizard zines comics in Emacs eshell.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Emacs eshell中的Wizard zines漫画 2 | #+URL: http://xenodium.com/wizard-zines-comics-eshell-util/index.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: raw 5 | #+DATE: [2019年 12月 11日 星期三 20:05:33 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 在 [[http://wizardzines.com][wizardzines.com]] 网站上,[[https://jvns.ca][Julia Evans]] 撰写了git, 网络, linux, 命令行工具等主题的超棒电子杂志. 其中有些杂志是付费的,有些杂志是免费的. 我这里不是托,只是个粉丝而已. 10 | 11 | 不久前, Julia [[https://twitter.com/b0rk/status/1192304892435738624][在tweet上l公布了]] 她创建的一个用来阅读相关主题漫画的工具. 我立即想到,这个有趣的工具很适合用 Emacs [[https://www.gnu.org/software/emacs/manual/html_mono/eshell.html][eshell]] 来实现. 12 | 13 | 自那以后, 我订阅了 [[https://wizardzines.com/saturday-comics/][wizardzines.com/saturday-comics]] 并收到了一些漫画 (awk, tar, 以及 bash 技巧). 我将这些漫画保存在本地 (以去掉文件扩展名的主题命名). 14 | 15 | #+begin_src shell 16 | ls -1 ~/Downloads/wizardzines-comics/ 17 | #+end_src 18 | 19 | #+BEGIN_EXAMPLE 20 | awk 21 | bash 22 | tar 23 | #+END_EXAMPLE 24 | 25 | 26 | 虽然没有经过实战测试, 但是下面这个elisp片段定义了一个 /ecomic/ 命令. 它会在eshell中以内联的方式显示漫画. 27 | 28 | #+begin_src emacs-lisp 29 | (require 'eshell) 30 | (require 'iimage) 31 | 32 | (defvar wizardzines-comics-path "~/Downloads/wizardzines-comics") 33 | 34 | (defun eshell/ecomic (&rest args) 35 | "Display command comic in ARGS. 36 | Note: ensure comic images live in `wizardzines-comics-path', named with 37 | command name and no extension." 38 | (eshell-eval-using-options 39 | "ecomic" args 40 | '((?h "help" nil nil "show this usage screen") 41 | :external "ecomic" 42 | :show-usage 43 | :usage "COMMAND 44 | 45 | Show COMMAND comic from Julia Evans' https://wizardzines.com/saturday-comics") 46 | (let* ((command (nth 0 (eshell-stringify-list (eshell-flatten-list args)))) 47 | (image-fpath (concat (file-name-as-directory 48 | (expand-file-name wizardzines-comics-path)) 49 | command))) 50 | (unless (file-exists-p image-fpath) 51 | (error "comic: \"%s\" not found :-(" command)) 52 | (eshell-buffered-print "\n") 53 | (add-text-properties 0 (length image-fpath) 54 | `(display ,(create-image image-fpath) 55 | modification-hooks 56 | (iimage-modification-hook)) 57 | image-fpath) 58 | (eshell-buffered-print image-fpath) 59 | (eshell-flush)))) 60 | #+end_src 61 | 62 | [[http://xenodium.com/images/wizard-zines-comics-eshell-util/ecomic.gif]] 63 | -------------------------------------------------------------------------------- /Eshell/cd到远程主机.org: -------------------------------------------------------------------------------- 1 | #+TITLE: cd到远程主机 2 | #+URL: http://irreal.org/blog/?p=5600 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: Eshell 5 | #+DATE: [2016-10-16 日 07:05] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | 我一直在尽可能的尝试用eshell替代真正的shell来完成我的工作. 这样以来我就能呆在Emacs中从而享受到Emacs带给我的便利了. 10 | 几天前,我在一篇博客中看到了一种关于cd的强大用法. 11 | 12 | 你可以像下面这样cd到远程主机的目录中: 13 | 14 | #+BEGIN_SRC sh 15 | cd /ssh:aineko:org 16 | #+END_SRC 17 | 18 | 这会通过tramp登陆到我的iMacs上的 =~/org= 目录中. 19 | 20 | 而 src_sh{cd /ssh:aineko:} 的效果就跟直接用ssh登陆到aineko这台主机上是一样的. 21 | 22 | 如果你想用另一个用户登陆远程主机,可以这么做: 23 | 24 | #+BEGIN_SRC sh 25 | cd /ssh:different_user@aineko: 26 | #+END_SRC 27 | 28 | 用这种方式虽然不能完全透明地像访问本地主机一样访问远程主机, 但它确实能让这些远程主机看起来就好像挂载到本地一样. 很好,很强大. 29 | -------------------------------------------------------------------------------- /Eshell/在Eshell中将目录加为书签.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Eshell中将目录加为书签 2 | #+AUTHOR: lujun9972 3 | #+TAGS: Eshell 4 | #+DATE: [2017-04-07 五 17:42] 5 | #+LANGUAGE: zh-CN 6 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 7 | 8 | 9 | 我一朋友曾经向我展示了一个很巧妙技巧,[[http://jeroenjanssens.com/2013/08/16/quickly-navigate-your-filesystem-from-the-command-line.html][能做到在bash中将目录加为书签]]. 10 | 这是个很棒的技巧,不过我经常使用的是Eshell,所以我想把这个技巧也移植到Eshell中来. 11 | (注意,Emacs允许你在Dired中将目录加为书签,但该书签不能用在Eshell中!) 12 | 这个技巧的关键在于共享一个书签数据库(其实就是一个名为 =~/.marks/= 的目录,用于存放指向实际目录的软链接). 13 | 14 | 下面这段函数就是Eshell的移植版本. 15 | #+BEGIN_SRC emacs-lisp 16 | (defun eshell/jump (mark) 17 | "Jump to a directory symlinked to by a file called ~/.marks/MARK." 18 | (eshell/cd (file-symlink-p (concat "~/.marks/" mark)))) 19 | #+END_SRC 20 | 注意这里的 =file-symlink-p= 可能有一定的误导性,当参数为指向文件的软链接时它会返回实际的文件路径,否则返回nil. 21 | 另外,这里的目录名称我也写死在了代码中了,也懒得去些其他的差错处理代码 – 虽然要做到这些也挺简单的. 22 | 23 | 这就已经很不错了,只是少了自动补全功能而已,而这个功能实现起来也完全没有难度: 24 | 25 | #+BEGIN_SRC emacs-lisp 26 | (defun pcomplete/jump () 27 | "Complete a command that wants a name of a file in ~/.marks." 28 | (pcomplete-here* (directory-files "~/.marks/"))) 29 | #+END_SRC 30 | 31 | 这就差不多搞定了! 32 | 33 | 虽说让Eshell支持Emacs书签会更 Emacs-y 一点,但是这种解决方案也有它的优势,那就同时支持 Eshell 和 bash. 34 | 更进一步,我们还可以为Emacs添加 =mark= 函数,这并不会太难. 35 | -------------------------------------------------------------------------------- /Eshell/在Eshell中设置别名.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Eshell中设置别名 2 | #+URL: http://mbork.pl/2018-07-16_Eshell_aliases 3 | #+AUTHOR: lujun9972 4 | #+TAGS: Eshell 5 | #+DATE: [2019-04-27 六 18:50] 6 | #+LANGUAGE: zh-CN 7 | #+STARTUP: inlineimages 8 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 9 | 10 | [[http://mbork.pl/2018-06-10_Git_diff_in_Eshell][前一段时间]], 我写过一篇文章关于让Eshell中启动的程序使用 =cat= 代替 =less= 来作为分页器的. 11 | 由于我一般只想让Git这样做, 而 Git 有一个 ==--no-pager= 选项, 因此更好的做法应该是自动为Git添加该选项. 12 | 13 | 嗯,事实证明Eshell拥有我们在bash中了解和热爱的特性: 别名. 14 | 唯一的缺点是,它们的工作方式略有不同(在bash中你使用等号而在Eshell中你使用空格, 然后别名的主体用但括号括起来). 15 | 16 | 总而言之, 你可以在Eshell中这样做 17 | 18 | #+begin_src shell 19 | alias git 'git --no-pager $*' 20 | #+end_src 21 | 22 | (别名会自动保存), 你立即就能享受您的Git新体验. 23 | -------------------------------------------------------------------------------- /advertisement/为什么Emacs是一款非常棒的文本编辑器.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 为什么Emacs是一款非常棒的文本编辑器 2 | #+URL: https://www.badykov.com/emacs/2018/07/31/why-emacs-is-a-great-editor/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: advertisement 5 | #+DATE: [2018年 09月 06日 星期四 11:36:08 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | [[https://i.imgur.com/J5Od0Xm.png]] 10 | 11 | 我使用emacs作为主文本编辑器,这经常带来争议。在这篇文章中,我将尝试解释为什么我认为emacs是一个很好的文本编辑器,以及它是如何帮助我每天提高工作效率的。 12 | 13 | * 历史 14 | 15 | 在使用emacs之前,我尝试了不同的文本编辑器(Atom、Brackets等),但主要使用的是Sublime。我使用Sublime有几年了。基于两个理由促使我学习Emacs。 16 | 17 | 第一次想到要提高文本编辑器使用技巧来自于我和同事在工作中解决某项任务时。 18 | 他使用Vim作为文本编辑器。在轮到他工作时,他的时间效率比我高得多,因为他的每个动作都有快捷键。 19 | 这段经历让我非常兴奋,和我用touchpad导航相比,他就像一个代码魔术师。 20 | 21 | 让我选择学习emacs而不是vim的原因是,在那段时间里Elixir社区中,emacs非常流行(现在仍然如此),人们在播客和博客中谈论它。 22 | 此外,Emacs也有一些编写Elixir的好插件([[https://github.com/tonini/alchemist.el][alchemist]], [[https://github.com/elixir-editors/emacs-elixir][elixir-mode]])。 23 | 24 | 现在我已经使用emacs超过1.5年了。 25 | 26 | * 优势 27 | 28 | 对我来说,使用emacs最重要的好处是我变得更有效率了。不用鼠标或触摸板,可以让我每个动作都节省几秒钟的时间,积累起来一天就是几个小时的时间。 29 | 我也可以学习Sublime的快捷键,但它的界面让我没有动力去和麼做。在emacs中则是另外一番场景:如果你不会快捷键,就无法使用它。 30 | 31 | 让我们看看Emacs特有的优势。您可以自定义Emacs中的一切:从它的外观到每个操作和行为。此外,emacs有自己的语言(emacs Lisp),您可以使用它来编写插件。 32 | 实际上,Emacs的配置文件也是用Emacs Lisp语言编写的。Emacs社区创建了很好的包,这些包在其他文本编辑器中都无法寻觅: 33 | 34 | - [[https://magit.vc/][Magit]] - 最好用的git接口. 35 | - [[https://orgmode.org/][Org-Mode]] -一种文档编辑、格式化和组织的模式,被设计用于在Emacs(自由软件文本编辑器)中进行记录、规划和创作。 36 | 37 | 有些人把emacs称为操作系统,因为它有很多内置的功能,也可以通过很多包来增强它的功能,甚至可以把emacs用作[[https://github.com/ch11ng/exwm][窗口管理器]]。 38 | 39 | 其他值得一提的事情: 40 | 41 | + [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Terminal-emulator.html][终端仿真]] 42 | + [[https://www.emacswiki.org/emacs/EmacsAsDaemon][服务器模式]] 43 | 44 | 45 | * 缺点 46 | 47 | emacs有一些缺点,特别是对于新用户: 48 | 49 | 1. 陡峭的学习曲线。您需要花几周的时间来学习emacs以提高使用它的效率。 50 | 2. 难用的键盘快捷键,尤其是对Mac用户来说,每一个快捷键上都有 =ctrl= 和 =alt= 键。但随着时间的推移,你会习惯的。 51 | 3. Emacs是单线程的,因此一个有bug的插件可能冻结您的Emacs进程。 52 | 53 | * 结论 54 | 55 | 我记得我的另一个同事说过,“我学习了emacs,所以我可以像弹吉他一样使用文本编辑器”。 这话有些道理。与音乐家使用的乐器一样,文本编辑器也是开发人员使用的工具。 56 | -------------------------------------------------------------------------------- /advertisement/抛弃宗教因素对比Vim和Emacs.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 抛弃宗教因素对比Vim和Emacs 2 | #+URL: https://feoh.org/2015/07/15/vim-versus-emacs-minus-the-religion/ 3 | #+AUTHOR: lujun9972, fannyxl 4 | #+CATEGORY: advertisement 5 | #+DATE: <2017-04-06 Thu> 6 | #+OPTIONS: ^:{} 7 | 8 | *Note*: 本文我最初是写在 [[http://www.quora.com/Text-Editors/Which-is-better-Vim-or-Emacs-Why/answer/Christopher-Patti][Quora]] 上的,然后经过润色一番后重新发布在这里. 9 | 10 | * 介绍 11 | 12 | 在中坚的技术团体中(In hard core technical circles),公然讨论这两个编辑器的优缺点是不被允许的. 在过去一段时间里,围绕着这个问题的争论引起了很严重的后果(当你回过头来思考为什么会有这么严重的后果时,会觉的这真是荒谬),并且持续了很长时间. 两边的支持者们很少倾听对方的理由,并且大多数的人都认为这是个二选一的问题. 说真的,我觉得这种观点完全没有任何意义. 13 | 14 | 我觉得我很适合回答这个问题,因为我使用这两种编辑器有大概25年了.(15年的时间一直在用Vim,还有10年的时间用emacs). 15 | 16 | 首先我要稍微批评一下你的问题[[[https://www.quora.com/Text-Editors/Text-Editors-Which-is-better-Vim-or-Emacs-Why/answer/Christopher-Patti][文本编辑器:哪个更好,Vim还是Emacs?为什么?]]]. 你问的问题太简单了,这样根本无法回答. Emacs 和 vi 都是很优秀的编辑器,它们都善于处理某一类的问题 - 但它们的强大体现在不同的方面,这使得它们在解决某些问题时要强于对方. 17 | 18 | * Vi/Vim 19 | 20 | 在我看来,若你的目标仅仅是编辑文本,那么Vim可能更适合你. 它的模式编辑(modal editing)能力使得它处理起文本来相当快速,同时它提供了大量的命令和快捷键来高效地进行定位和移动文本,这使得它的潜能无可限量. 21 | 22 | 近年来,各种扩展技术被应用于Vi(a-la Vim),这使得它可以以各种不同的方式进行扩展,但是以我个人的经验来看, 它的这种编辑范式和机制限制了它的扩展性,使它难于完成你像要作的事情. 23 | 24 | 例如,对我来说压倒骆驼的最后一根稻草是想为Vim添加类似IDE那样的自动补全与重构方法/成员变量的功能. 目前在Vim上有了许多的解决方案,但是它们要么完全无法工作,要么需要安装那些难以构建与安装的C共享库,而这使得我的Vim变得很不稳定. 当你知道你的你的编辑器可能在编辑当中崩溃时,你可的仔细考虑一下了. 25 | 26 | 注意,上面的场景其实与编辑文本根本没半点关系. 这是将Vim强制改造成IDE的必然结果. 27 | 28 | 有些人提到Vi/Vim哪都有,这是它的一大优势. 但Vi/Vim只是默认安装于UNIX系统上而已,然而这并没有什么卵用. 29 | 30 | Vim用户建议看一下[[http://www.vimcasts.com][Vimcasts]] 以及Drew Neil's的那本[[https://pragprog.com/book/dnvim/practical-vim][Practical Vim]]. 31 | 32 | * Emacs 33 | 34 | 我个人更倾向于把Emacs看成是一个极度强力的编程环境而不仅是一个文本编辑器. 只是它同时被优化用于进行文本工作,其结果是,它也具有很强大的文本编辑功能了. 35 | 36 | 我知道,看起来我似乎只是在玩文字游戏,但是相信我,我并没有. 如果你想创建一个完全适合你需求的的超级强力的编程环境,那么Emacs无疑是你的最佳选择. 37 | 38 | Emacs十分擅长于控制子进程, 因此,如果你使用Python编程,emacs会在内部调用Python解释器. 你可能会觉得奇怪,为什么要这样作呢? 这是因为,接下来你可以一边编码,一边试运行一些代码并看到运行的结果. Emacs甚至可以在远程机器上运行Python解释器然后将它连接到你的编辑session中. 39 | 40 | Emacs的核心是emacs lisp - elisp是emacs超能力的来源. 它是emacs的唯一扩展语言,但是它可以用来作任何事情. 即使不学习elisp,你也可以高效的使用emacs, 但代价是,既然你跟普通大众一样,那么当你发现某个mode或其他包在某些地方不符合你的需求时,你会变得不知所措🙂 41 | 42 | 如果你乐意,你可以使用emacs作为你的整个工作环境 - 你可以用它编辑文本,撸代码,聊天,对源码进行版本控制(例如Git)等等几乎任何事情. 43 | 44 | 这也是许多非emacs用户攻击的地方:"它根本不是个编辑器,它就是个操作系统!". 要我说,没错,但这才是Emacs强力的地方啊🙂 45 | 46 | 比较好的Emacs用户的入门资料有 - [[http://pragmaticemacs.com/][Pragmatic Emacs]] 以及both stellar的[[https://www.masteringemacs.org/][Mastering Emacs]] 这本书(同时也是网站). 47 | 48 | * 总结 49 | 50 | 答案很长,但是考虑到问题本身很敏感也很复杂,因此我想这个长度是正常的. 51 | 52 | 若你主要是编辑一些存文本,或者只需作少量的编码工作,或者只是编辑一些静态配置文件,又或者你需要所用的编辑器在所有的UNIX操作系统中都有安装,那么Vim无疑是你的最佳选择. 53 | 54 | 若你的需求很复杂,并且时常需要自己来突破编辑器的限制, 需要编辑器提供类似IDE那样的功能, 又或者你是个开发狂热者,喜欢编码并且需要你的环境提供最大化的定制能力,则直接用Emacs吧 55 | 56 | 最后,抛开那些辩论和信仰,只要试一下哪种编辑器会使你更有效率就行了. 又或者可以像我一样,同时使用两种编辑器! 通常当我在服务器上要快速改个什么东西,我会用vi;当我在我的台式机/笔记本上进行大量的编辑时,我会使用emacs. 57 | 58 | 工具就是工具,哪个好用就用哪个. 59 | 60 | [ Update 07/16/2015: 有热心的读者提醒我使用 [[http://www.emacswiki.org/emacs/Evil][evil-mode]] 可以兼顾两者的优点🙂 它为Emacs提供了与vim完全兼容的操作方式,并且已经很完善了. 它工作得很好,而且我在刚从Vi转到Emacs时用过它,我由衷地推荐.] 61 | 62 | -------------------------------------------------------------------------------- /advertisement/用来记笔记的三个 Emacs modes.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 用来记笔记的三个 Emacs modes 2 | #+URL: https://opensource.com/article/18/7/emacs-modes-note-taking 3 | #+AUTHOR: lujun9972 4 | #+TAGS: advertisement 5 | #+DATE: [2019-04-26 五 21:25] 6 | #+LANGUAGE: zh-CN 7 | #+STARTUP: inlineimages 8 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 9 | 10 | [[https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/notebook-writing-pen.jpg]] 11 | 12 | 不管你从事哪种工作, 你都无可避免地需要记笔记. 而且可能还不是一点点. 现在这年头,大家都开始以数字的形式来记笔记了. 13 | 14 | 开源软件爱好者有多种途径来以电子的方式记下他们的创意, 想法和研究过程. 你可以使用 [[https://opensource.com/alternatives/evernote][网页工具]]. 你可以使用 [[https://opensource.com/life/16/9/4-desktop-note-taking-applications][桌面应用]]. 或者, 你也可以 [[https://opensource.com/article/18/3/command-line-note-taking-applications][使用命令行工具]]. 15 | 16 | 如果你使用 [[https://www.gnu.org/software/emacs/][Emacs]](伪装成文本编辑器的强力操作系统),有多个 mode 可以帮你有效地记录笔记. 我们这里列举三个例子. 17 | 18 | * Deft 19 | 20 | [[https://opensource.com/sites/default/files/uploads/deft.png]] 21 | 22 | 在少数情况下,我被迫需要使用Mac, 有一个工具是我不能缺少的: [[http://brettterpstra.com/projects/nvalt/][nvALT]] 笔记应用. [[https://jblevins.org/projects/deft/][Deft mode]] 为Emacs带来了 nvALT 式的体验. 23 | 24 | Deft 将你的笔记以文本文件的形式存储在电脑中的某个文件夹中. 当你进入 Deft mode, 你会看到一系列的笔记及其摘要. 这些摘要其实就是文本文件的第一行. 若第一行是 Markdown, LaTeX, 甚至 Emacs Org mode 格式的话, Deft 会忽略掉这些格式而只显示文本内容. 25 | 26 | 要打开笔记, 只需要向下滚动到该笔记的位置然后按下回车即可. 然而Deft 不仅仅只是这样. 根据 Deft 开发者 Jason Blevins 的说法, 它的 /主要操作时搜索和过滤/. 27 | Deft 的实现方式简单而有效. 输入关键字然后 Deft 会只显示标题中包含关键字的笔记. 这在你要从大量笔记中找到某条笔记时非常有用. 28 | 29 | * Org mode 30 | 31 | [[https://opensource.com/sites/default/files/uploads/orgmode.png]] 32 | 33 | 如果本文没有包含 [[https://orgmode.org/][Org mode]] 的话,那么我可能会被人所诟病. 为什么? 它可以说是Emacs中最灵活、使用最广泛的记录笔记的方式了. 34 | 以正确的方式使用它, Org mode 可以极大地增强记笔记的能力. 35 | 36 | Org mode 的主要优势在于它组织笔记的方式. 在 Org mode 中, 一个笔记文件被组织成一个巨大的大纲. 37 | 每个章节就是大纲里的一个节点, 你可以对它进行展开和折叠. 38 | 这些章节又可以有子章节,这些子章节也可以暂开和折叠. 39 | 这不仅使你一次只关注于某个章节, 而且可以让你浏览整个大纲. 40 | 41 | 你可以在多个章节之间 [[https://orgmode.org/org.html#Hyperlinks][进行互联]], 无需通过剪切和复制就能快速移动章节, 以及 [[https://orgmode.org/org.html#Attachments][附加文件]] 到笔记中. 42 | Org mode 支持带格式的字符和表格. 43 | 如果你需要转换笔记到其他格式, Org mode 也有大量的[[https://orgmode.org/org.html#Exporting][导出选项]]. 44 | 45 | * Howm 46 | 47 | [[https://opensource.com/sites/default/files/uploads/howm.png]] 48 | 49 | 当我使用Emacs已经成为一种习惯时, [[https://howm.osdn.jp/][howm]] 马上就成为我严重依赖的mode之一了. 虽然我特别喜欢使用Org mode, 但howm依然占有一席之地. 50 | 51 | Howm 就好像时一个小维基似得. 你可以创建笔记和任务列表,还能在他们之间创建链接. 52 | 通过输入或点击某个链接, 你可以在笔记之间跳转. 53 | 如果你需要, 还可以使用关键字为笔记添加标签. 54 | 不仅如此, 你可以对笔记进行搜索、排序和合并. 55 | 56 | Howm 不是最漂亮的 Emacs mode, 它也没有最好的用户体验. 57 | 它需要你花一点时间来适应它. 而一旦你适应了它, 记录和查找笔记就是轻而易举的事情了. 58 | -------------------------------------------------------------------------------- /calc/emacs-calc.org: -------------------------------------------------------------------------------- 1 | #+TITLE: emacs-calc 2 | #+URL: http://www.johndcook.com/blog/2010/10/11/emacs-calc/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: raw 5 | #+DATE: [2016-07-06 三 11:19] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | Emacs 带有一个十分精密的科学计算器. 就像Emacs的其他功能一样,它即强大又怪异. 10 | 11 | =calc= 模块自22版开始就内置于Emacs了. [[http://www.xemacs.org/Documentation/packages/html/calc.html][calc manual]] 的文档很全面,但看起来也不容易懂. 希望本文能让你看起manual来更轻松一些. 12 | 13 | manual上说可以通过 =M-# .= 来打开calc,但我按了没有反应,倒是可以通过 =M-x calc= 来打开calc. 14 | 15 | Calc有两种模式: RPN ([[http://en.wikipedia.org/wiki/Reverse_Polish_Notation][逆波兰表示法]]) 和代数表示法. calc默认使用RPN模式并且其命令也很简介,一般也就用一两个字符来表示. 大多数人更习惯于代数表示法. 代数表示法模式以引号开头,且该模式下的命令使用更长,更具有描述性的名字来表示. 16 | 17 | 举个例子,假设你想知道5的余弦是多少, 在RPN模式下,你需要按下 =5= 回车,然后按下 =C= 即可,而在代数模式下,需要输入 'cos(5) 再回车. 18 | 19 | 下面我以 =gamma= 函数为例,讲解怎样看懂manual(how to interpret the manual). manual上关于 =gamma= 的说明如下: 20 | 21 | #+BEGIN_EXAMPLE 22 | The f g (calc-gamma) [gamma] command computes the Euler gamma function. … 23 | #+END_EXAMPLE 24 | 25 | 这句话的意思是,在RPN模式下,你先输入 =gamma= 函数的参数,比如说5,然后按下 =fg= 就得到结构了. (注意这里不是 =f g=). 你也可以先输入5,然后输入 =M-x calc-gamma=. 在代数模式下,你需要输入的则是 'gamma(5). 26 | 27 | 也就是说,manual书写的格式为: 28 | 29 | #+BEGIN_EXAMPLE 30 | RPN keystrokes (lisp function) [algebraic syntax]. 31 | #+END_EXAMPLE 32 | 33 | calc模块的功能及其强大,其支持 符号运算,矩阵运算,图等等功能. — 我一辈子都用不了这么多功能. 在我看来,使用calc最大的好处是可以不用离开Emacs即可快速计算. 34 | 我觉得不适宜用calc来进行复杂的计算,理由有二: 1, 我需要中断手头的事情来查询calc中作相关计算的函数是什么. 2, 我不希望在calc中计算到一半时,发现缺少相关工具而不得不又切换到使用SciPyI,Mathematica或R上去. 35 | 36 | Related posts: 37 | 38 | * [[http://www.johndcook.com/blog/2010/07/14/bc-math-library/][Three surprises with bc]] 39 | * [[http://www.johndcook.com/blog/2010/04/01/giving-emacs-another-try/][Giving Emacs another try]] 40 | -------------------------------------------------------------------------------- /dired/在Dired中批量修改文件权限.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Dired中批量修改文件权限 2 | #+URL: http://pragmaticemacs.com/emacs/batch-edit-file-permissions-in-dired/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2017-02-22 周三 11:39] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 我之前提到过,dired的编辑功能(即wdired)能够很方便地完成像 [[http://pragmaticemacs.com/emacs/dired-redirect-symbolic-links/][修改软连接指向]] 这一类的操作. 然后我最近又发现,只要在你的 [[http://pragmaticemacs.com/emacs/editing-your-emacs-config-file/][emacs配置文件中]] 加入: 10 | 11 | #+BEGIN_SRC emacs-lisp 12 | ;; allow editing file permissions 13 | (setq wdired-allow-to-change-permissions t) 14 | #+END_SRC 15 | 16 | 这么一句话,你就可以使在dired buffer中直接修改文件属性了. 17 | 18 | 下面动画展示了我是怎么为某目录下的所有的pdf文件设置用户组组的写权限的,其中涉及到许多插件: 19 | 20 | 1. 使用 [[http://pragmaticemacs.com/emacs/dynamically-filter-directory-listing-with-dired-narrow/][dired-narrow]] 来过滤出pdf文件 21 | 2. 按下 =C-x C-q= 来进入dired的可编辑模式 22 | 3. 移动到第一行的用户组权限位置,然后使用 [[http://pragmaticemacs.com/emacs/multiple-cursors/][multiple cursors]] 来为每一行都加上虚拟光标 23 | 4. 按下 =w= 键来设置写权限, 按下 =RET= 退出 multiple cursors, 然后按下 =C-c C-c= 确认修改 24 | 25 | [[https://i1.wp.com/pragmaticemacs.com/wp-content/uploads/2017/02/dired-file-permissions.gif?w=620]] 26 | -------------------------------------------------------------------------------- /dired/在Emacs的dired和tramp中异步运行rsync.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Emacs的dired和tramp中异步运行rsync 2 | #+URL: https://vxlabs.com/2018/03/30/asynchronous-rsync-with-emacs-dired-and-tramp/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: dired 5 | #+DATE: [2019-03-15 五 22:09] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | [[https://truongtx.me/about.html][Trần Xuân Trường]] 写的 [[https://truongtx.me/tmtxt-dired-async.html][tmtxt-dired-async]]  是一个不为人知的Emacs包, 它可以扩展 dired(Emacs内置的文件管理器), 使之可以异步地运行 rsync 等其他命令 (例如zip, unzip, downloading). 10 | 11 | 这意味着你可以拷贝成G的目录而不影响Emacs的其他任务. 12 | 13 | 它的一个功能时让你可以通过 =C-c C-a= 从不同位置添加任意多的文件到一个等待列表中, 然后按下 =C-c C-v= 异步地使用rsync将整个等待列表中的文件同步到目标目录中 . 光这个功能就值得一试了. 14 | 15 | 例如这里将 arduino 1.9 beta 存档同步到另一个目录中: 16 | 17 | [[https://i0.wp.com/vxlabs.com/wp-content/uploads/2018/03/rsync-arduino-zip.png]] 18 | 19 | 整个进度完成后, 底部的窗口会在5秒后自动退出. 下面时异步解压上面的arduino存档后出现的另一个会话: 20 | 21 | [[https://i1.wp.com/vxlabs.com/wp-content/uploads/2018/03/progress-window-5s.png]] 22 | 23 | 这个包进一步增加了我dired配置的实用性. 24 | 25 | 我刚刚贡献了 [[https://github.com/tmtxt/tmtxt-dired-async/pull/6][一个 pull request 来允许 tmtxt-dired-async 同步到远程tramp目录中]], 而且我立即使用该功能来将成G的新照片传输到 Linux 服务器上. 26 | 27 | 若你想配置 tmtxt-dired-async, 下载 [[https://github.com/tmtxt/tmtxt-async-tasks][tmtxt-async-tasks.el]] (被依赖的库) 以及 [[https://github.com/tmtxt/tmtxt-dired-async][tmtxt-dired-async.el]] (若你想让它支持tramp,请确保我的PR以及被合并) 到 =~/.emacs.d/= 目录中,然后添加下面配置: 28 | 29 | #+begin_src emacs-lisp 30 | ;; no MELPA packages of this, so we have to do a simple check here 31 | (setq dired-async-el (expand-file-name "~/.emacs.d/tmtxt-dired-async.el")) 32 | (when (file-exists-p dired-async-el) 33 | (load (expand-file-name "~/.emacs.d/tmtxt-async-tasks.el")) 34 | (load dired-async-el) 35 | (define-key dired-mode-map (kbd "C-c C-r") 'tda/rsync) 36 | (define-key dired-mode-map (kbd "C-c C-z") 'tda/zip) 37 | (define-key dired-mode-map (kbd "C-c C-u") 'tda/unzip) 38 | 39 | (define-key dired-mode-map (kbd "C-c C-a") 'tda/rsync-multiple-mark-file) 40 | (define-key dired-mode-map (kbd "C-c C-e") 'tda/rsync-multiple-empty-list) 41 | (define-key dired-mode-map (kbd "C-c C-d") 'tda/rsync-multiple-remove-item) 42 | (define-key dired-mode-map (kbd "C-c C-v") 'tda/rsync-multiple) 43 | 44 | (define-key dired-mode-map (kbd "C-c C-s") 'tda/get-files-size) 45 | 46 | (define-key dired-mode-map (kbd "C-c C-q") 'tda/download-to-current-dir)) 47 | #+end_src 48 | 49 | 祝你开心! 50 | -------------------------------------------------------------------------------- /dired/在dired中使用rsync同步文件.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在dired中使用rsync同步文件 2 | #+URL: http://oremacs.com/2016/02/24/dired-rsync/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-08-02 周二 10:15] 6 | #+OPTIONS: ^:{} 7 | 8 | 下面这段代码是我很久以前从网上找到的(源地址似乎以及不能再访问了),我经常会用到这段代码: 9 | 10 | #+BEGIN_SRC emacs-lisp 11 | ;;;###autoload 12 | (defun ora-dired-rsync (dest) 13 | (interactive 14 | (list 15 | (expand-file-name 16 | (read-file-name 17 | "Rsync to:" 18 | (dired-dwim-target-directory))))) 19 | ;; store all selected files into "files" list 20 | (let ((files (dired-get-marked-files 21 | nil current-prefix-arg)) 22 | ;; the rsync command 23 | (tmtxt/rsync-command 24 | "rsync -arvz --progress ")) 25 | ;; add all selected file names as arguments 26 | ;; to the rsync command 27 | (dolist (file files) 28 | (setq tmtxt/rsync-command 29 | (concat tmtxt/rsync-command 30 | (shell-quote-argument file) 31 | " "))) 32 | ;; append the destination 33 | (setq tmtxt/rsync-command 34 | (concat tmtxt/rsync-command 35 | (shell-quote-argument dest))) 36 | ;; run the async shell command 37 | (async-shell-command tmtxt/rsync-command "*rsync*") 38 | ;; finally, switch to that window 39 | (other-window 1))) 40 | 41 | (define-key dired-mode-map "Y" 'ora-dired-rsync) 42 | #+END_SRC 43 | 44 | 它能做到让你拷贝大量的文件和目录而不会卡住Emacs,而且还带了个进度条. 45 | 46 | 感谢你tmtxt,创建这段代码的神秘人. 希望还能再看到你重开博客. 47 | -------------------------------------------------------------------------------- /elisp-common/Emacs的process-filter函数中的竞态条件.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Emacs的process-filter函数中的竞态条件 2 | #+URL: http://blog.jorgenschaefer.de/2014/05/race-conditions-in-emacs-process-filter.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2017-02-07 周二 16:10] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 10 | 竞态条件总是很有趣. 有其当它出现在类似Emacs这样的单线程程序中时就更显诡异了. 不过我已经遇到这个问题两次了. 11 | 12 | 当Emacs从process(译者注:由于Emacs中的process即可以是进程,也可以是一个网络链接,因此还是保持process不翻译)中读取数据时,会执行相应的filter函数. Emacs调用这个filter函数处理获得的数据后,再进行其他的工作. 13 | 然而在某些情况下,可能会发生在某个filter函数正在运行时运行另一个filter函数的情况. 触发的条件貌似是随机的. 14 | 15 | Emacs只要一从process中读取到数据就会运行filter函数,即时这时候正在运行另一个filter函数. 16 | 很多情况下都会造成Emacs从process中读取数据. 即时你知道所有能触发Emacs从process中读取数据的函数也没什么用, 因为在大多数时候你无法控制,甚至无法知道Emacs运行的代码有哪些. 17 | 比如, 当你创建某个mode的buffer时,就会触发该mode的hook,而这个hook可能运行几乎任何代码. 18 | 19 | 我首次遇到这个问题是在使用 [[https://github.com/jorgenschaefer/circe][Circe]] 时发现的, 我用它作为Emacs上的IRC客户端. 20 | 在一个filter函数中,它会创建一个某个特定mode下的buffer. 而该mode可能会激活flyspell(Emacs的拼写检查工具). 21 | flyspell又会运行一个新来进行拼写检查. 运行新进程又会导致Emacs从process中读取数据,运行filter函数. 22 | 这个问题很难调试,因为只有当用户刚好在极短的事件内连续收到两条消息时才会触发. 23 | 24 | 你可以在Circe代码中看 [[https://github.com/jorgenschaefer/circe/blob/d69c4c0f781aab1fe53a82247099433ff52805b1/lisp/circe.el#L675-L688][我对该问题的说明]] 25 | 26 | 然后我又在 [[https://github.com/jorgenschaefer/elpy][Elpy]] 中遇到了 [[https://github.com/jorgenschaefer/elpy/issues/234#issuecomment-44249993][相同的问题]], 它是我的Python开发环境. 27 | 让我没想到的是, Emacs的 =process-send-string= 不仅仅只是发送数据,它还会同时从process中读取并处理数据. 28 | 这个问题出现的场景是,正在运行一个初始化函数,然后设置一个标志来标示初始化过程已经做过了,没有必要再进行以此初始化了. 29 | 然而该初始化函数会王process发送数据. 而该process的filter函数由会重新调用该初始化函数. 30 | 该初始化函数发现尚未初始化后又会向process发送数据,而这又会触发从process读取数据的过程,然后又一次触发初始化函数,再发送数据... 31 | 如此死循环下去最后栈溢出了. 32 | 33 | 你问怎么办? 只能使用一个动态作用域的"锁"了 (当然不是真的缩,它并不会阻塞程序的运行,不过也差不多吧). 34 | 35 | #+BEGIN_SRC emacs-lisp 36 | (defvar my-lock nil) 37 | (when (not my-lock) 38 | (let ((my-lock t)) 39 | ...)) 40 | #+END_SRC 41 | 42 | 对于像Emacs这样的单线程应用, 这样搞看起来没什么用,但事实证明它的能起作用. 43 | -------------------------------------------------------------------------------- /elisp-common/Fractal Rendering in Emacs - null program.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Emacs中的分形渲染 2 | #+URL: http://nullprogram.com/blog/2012/09/14/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2018年 03月 07日 星期三 17:12:42 CST] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 借助 Emacs' =image-mode= 以及方便的 [[http://en.wikipedia.org/wiki/Netpbm_format][Netpbm 格式]] 可以在Emacs中用Elisp来生成并渲染图像. 下面这个函数会生成一个 [[http://en.wikipedia.org/wiki/Sierpi%C5%84ski_carpet][Sierpinski 地毯]] 并在buffer中显示. 10 | 11 | #+begin_src emacs-lisp 12 | (defun sierpinski (s) 13 | (pop-to-buffer (get-buffer-create "*sierpinski*")) 14 | (fundamental-mode) (erase-buffer) 15 | (labels ((fill-p (x y) 16 | (cond ((or (zerop x) (zerop y)) "0") 17 | ((and (= 1 (mod x 3)) (= 1 (mod y 3))) "1") 18 | (t (fill-p (/ x 3) (/ y 3)))))) 19 | (insert (format "P1\n%d %d\n" s s)) 20 | (dotimes (y s) (dotimes (x s) (insert (fill-p x y) " ")))) 21 | (image-mode)) 22 | #+end_src 23 | 24 | 建议用三次暮作为参数来调用它, 25 | 26 | #+begin_src emacs-lisp 27 | (sierpinski (expt 3 5)) 28 | #+end_src 29 | 30 | [[http://nullprogram.com/img/fractal/sierpinski.png][http://nullprogram.com/img/fractal/sierpinski-thumb.png]] 31 | 32 | 下面这个例子你应该会 [[https://nullprogram.com/blog/2007/10/01/][比较眼熟]]. 使用的是一样的技术, 33 | 34 | #+begin_src emacs-lisp 35 | (defun mandelbrot () 36 | (pop-to-buffer (get-buffer-create "*mandelbrot*")) 37 | (let ((w 400) (h 300) (d 32)) 38 | (fundamental-mode) (erase-buffer) 39 | (set-buffer-multibyte nil) 40 | (insert (format "P6\n%d %d\n255\n" w h)) 41 | (dotimes (y h) 42 | (dotimes (x w) 43 | (let* ((cx (* 1.5 (/ (- x (/ w 1.45)) w 0.45))) 44 | (cy (* 1.5 (/ (- y (/ h 2.0)) h 0.5))) 45 | (zr 0) (zi 0) 46 | (v (dotimes (i d d) 47 | (if (> (+ (* zr zr) (* zi zi)) 4) (return i) 48 | (psetq zr (+ (* zr zr) (- (* zi zi)) cx) 49 | zi (+ (* (* zr zi) 2) cy)))))) 50 | (insert-char (floor (* 256 (/ v 1.0 d))) 3)))) 51 | (image-mode))) 52 | #+end_src 53 | 54 | [[https://nullprogram.com/img/fractal/elisp-mandelbrot.png]] 55 | 56 | 我们可以用colormap函数为它染上色, 57 | 58 | #+begin_src emacs-lisp 59 | (defun colormap (v) 60 | "Given a value between 0 and 1.0, insert a P6 color." 61 | (dotimes (i 3) 62 | (insert-char (floor (* 256 (min 0.99 (sqrt (* (- 3 i) v))))) 1))) 63 | #+end_src 64 | 65 | [[https://nullprogram.com/img/fractal/elisp-mandelbrot-color.png]] 66 | 67 | 我脑中一直有一个想法(但是可能永远不会去实现它了)就是创建一个Elisp的小型图库. 它的实现基础就是该技术. 若这种支持被编译在Emacs内, 则Emacs甚至可以在buffer中渲染 SVG 图片, 这样创建一个丰富的图形库并不困难. 而且, 不像纯 Elisp, 它会很快. 68 | 69 | 70 | -------------------------------------------------------------------------------- /elisp-common/Part 1: 运行Lisp的基础知识.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Part 1: 运行Lisp的基础知识 2 | #+URL: http://dantorop.info/project/emacs-animation/lisp1.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2020年 02月 10日 星期一 10:09:51 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | * 输入 =M-x ielm= 进入交互式Lisp模式 9 | :PROPERTIES: 10 | :CUSTOM_ID: type-m-x-ielm-to-get-into-the-interactive-lisp-mode 11 | :END: 12 | 13 | 你应该看到类似这样的东西: 14 | 15 | #+BEGIN_EXAMPLE 16 | ,*** Welcome to IELM *** Type (describe-mode) for help. 17 | ELISP> 18 | #+END_EXAMPLE 19 | 20 | * 来测试一下 21 | :PROPERTIES: 22 | :CUSTOM_ID: to-test-it-out 23 | :END: 24 | 25 | 在引号中输入一个数字或一些文本,它应该会回显给你: 26 | 27 | #+BEGIN_EXAMPLE 28 | ELISP> 23 29 | 23 30 | ELISP> "testing" 31 | "testing" 32 | ELISP> 33 | #+END_EXAMPLE 34 | 35 | * 使用=setq=设置变量 36 | :PROPERTIES: 37 | :CUSTOM_ID: setting-variables-with-setq 38 | :END: 39 | 40 | #+BEGIN_EXAMPLE 41 | ELISP> (setq foo 25) 42 | 25 43 | ELISP> (+ foo 1) 44 | 26 45 | ELISP> foo 46 | 25 47 | ELISP> (setq foo (+ foo 1)) 48 | 26 49 | ELISP> foo 50 | 26 51 | ELISP> 52 | #+END_EXAMPLE 53 | 54 | * 使用 =while= 循环(from scratch) 55 | :PROPERTIES: 56 | :CUSTOM_ID: looping-with-while-from-scratch 57 | :END: 58 | 59 | #+BEGIN_EXAMPLE 60 | ELISP> (setq foo 10) 61 | 10 62 | ELISP> (while (> foo 0) 63 | (insert "testing") 64 | (newline) 65 | (setq foo (- foo 1))) 66 | nil 67 | ELISP> testing 68 | testing 69 | testing 70 | testing 71 | testing 72 | testing 73 | testing 74 | testing 75 | testing 76 | testing 77 | #+END_EXAMPLE 78 | 79 | * 使用=dotimes=循环打印 80 | :PROPERTIES: 81 | :CUSTOM_ID: printing-with-loops-within-loops-with-dotimes 82 | :END: 83 | 84 | #+BEGIN_EXAMPLE 85 | ELISP> (dotimes (outer-count 10) 86 | (dotimes (inner-count outer-count) 87 | (insert "?")) 88 | (insert "! ")) 89 | nil 90 | ELISP> ! ?! ??! ???! ????! ?????! ??????! ???????! ????????! ?????????! 91 | #+END_EXAMPLE 92 | 93 | * 请求答复 94 | :PROPERTIES: 95 | :CUSTOM_ID: asking-for-a-response 96 | :END: 97 | 98 | #+BEGIN_EXAMPLE 99 | ELISP> (setq foo (read)) 100 | #+END_EXAMPLE 101 | 102 | * 使用 =defun= 103 | :PROPERTIES: 104 | :CUSTOM_ID: using-defun 105 | :END: 106 | 107 | #+BEGIN_EXAMPLE 108 | ELISP> (defun repeater (num-times print-what) 109 | (dotimes (counter num-times) 110 | (insert print-what))) 111 | repeater 112 | ELISP> (repeater 10 "!?") 113 | nil 114 | ELISP> !?!?!?!?!?!?!?!?!?!? 115 | #+END_EXAMPLE 116 | 117 | 要在 =ielm= 外部运行 =defun= 或其他Lisp,请输入 =M-:= 会在屏幕底部显示Lisp提示符,然后输入所需的Lisp。按向上箭头键查找以前键入的命令。 118 | 119 | 如果Emacs报告了一个错误,您将看到一个 =Backtrace= buffer出现,它的顶部一行写着 =Debugger entered=. 键入 =q= 退出,然后重试。 120 | -------------------------------------------------------------------------------- /elisp-common/Part 2: 简单的ASCII艺术.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Part 2: 简单的ASCII艺术 2 | #+URL: http://dantorop.info/project/emacs-animation/lisp2.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2020年 02月 10日 星期一 10:09:08 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 10 | * 使用 =random= 11 | :PROPERTIES: 12 | :CUSTOM_ID: using-random 13 | :END: 14 | 15 | #+BEGIN_EXAMPLE 16 | ELISP> (dotimes (i (random 10)) 17 | (dotimes (j (random (+ i 5))) 18 | (insert "&")) 19 | (insert "^")) 20 | nil 21 | ELISP> &&^&&&&^&&&&^&&&&^&&&&&&&&^&&&&&&&^ 22 | #+END_EXAMPLE 23 | 24 | * 使用 =if= 偶尔插入换行符 25 | :PROPERTIES: 26 | :CUSTOM_ID: using-if-to-sometimes-insert-newlines 27 | :END: 28 | 29 | #+BEGIN_EXAMPLE 30 | ELISP> (dotimes (i (random 30)) 31 | (if (= (random 5) 0) 32 | (newline)) 33 | (dotimes (j (random 10)) 34 | (insert "?")) 35 | (insert " < ")) 36 | nil 37 | ELISP> ?? < 38 | ?? < ?? < < ?? < ????? < 39 | ???????? < ?????? < 40 | < ??????? < 41 | ????????? < ???????? < ??????? < 42 | < ???????? < ?? < ???????? < ???? < ????????? < ???????? < ??? < ????????? < ?? < ????? < ??? < 43 | ??? < ????????? < 44 | #+END_EXAMPLE 45 | 46 | * 使用 =sit-for= 控制时间 47 | :PROPERTIES: 48 | :CUSTOM_ID: using-sit-for-to-control-time 49 | :END: 50 | 51 | #+BEGIN_EXAMPLE 52 | ELISP> (dotimes (i 10) 53 | (dotimes (j (random 20)) 54 | (insert ",,")) 55 | (insert "#") 56 | (sit-for (* 0.1 (random 4)))) 57 | nil 58 | ELISP> ,,,,,,,,,,#,,,,##,,,,,,,,#,,,,,,,,,,,,#,,,,,,,,,,#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#,,,,,,,,,,,,,,,,,,,,,,,,,,,,#,,,,,,,,,,,,,,# 59 | #+END_EXAMPLE 60 | 61 | * 插入的同时移动光标 62 | :PROPERTIES: 63 | :CUSTOM_ID: moving-cursor-while-inserting 64 | :END: 65 | 66 | #+BEGIN_EXAMPLE 67 | ELISP> (dotimes (i 20) 68 | (dotimes (j (random 5)) 69 | (insert ",,")) 70 | (goto-char (- (point) (random 4))) 71 | (insert "#") 72 | (if (= (random 3) 0) 73 | (newline)) 74 | (sit-for (* 0.1 (random 3))))# 75 | ,,,,,,,,# 76 | ,,,,,# 77 | ,,,,,,#,,##,,,#,,# 78 | ,,,,#,,,#,,,,,,#,,,#,# 79 | ,,,#,,,#,,,,,,,,,,,# 80 | #,,#,,,#,,,,,,,,#,,,,, 81 | nil 82 | ELISP> 83 | #+END_EXAMPLE 84 | 85 | * 上面的简易版本 86 | :PROPERTIES: 87 | :CUSTOM_ID: a-slightly-easier-way-to-do-the-same 88 | :END: 89 | 90 | #+BEGIN_EXAMPLE 91 | ELISP> (dotimes (i 20) 92 | (dotimes (j (random 5)) 93 | (insert ",,")) 94 | (backward-char (random 4)) 95 | (insert "#") 96 | (if (= (random 3) 0) 97 | (newline)) 98 | (sit-for (* 0.1 (random 3)))) 99 | nil 100 | ELISP> ,,##,,,# 101 | #,,,,,,,,## 102 | ,,#,,,,,,#,#,,,,,##,#,#,,,#,,,,,,# 103 | # 104 | ,,#,,,,,,,,, 105 | ,,,#,#,,,,,,,,,,, 106 | ,#, 107 | #+END_EXAMPLE 108 | -------------------------------------------------------------------------------- /elisp-common/Part 3: 列表与颜色.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Part 3: 列表与颜色 2 | #+URL: http://dantorop.info/project/emacs-animation/lisp3.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2020年 02月 10日 星期一 10:08:06 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | * 遍历list 10 | :PROPERTIES: 11 | :CUSTOM_ID: going-through-a-list 12 | :END: 13 | 14 | #+BEGIN_EXAMPLE 15 | ELISP> (setq bar '("do" "re" "mi" "fa")) 16 | ("do" "re" "mi" "fa") 17 | 18 | ELISP> (dolist (note bar) 19 | (insert note)) 20 | nil 21 | ELISP> doremifa 22 | #+END_EXAMPLE 23 | 24 | * List 中的 list 25 | :PROPERTIES: 26 | :CUSTOM_ID: lists-within-lists 27 | :END: 28 | 29 | #+BEGIN_EXAMPLE 30 | ELISP> (setq foo '(("hello" 5) 31 | ("bye" 2) 32 | ("later" 7))) 33 | (("hello" 5) 34 | ("bye" 2) 35 | ("later" 7)) 36 | 37 | ELISP> (dolist (item foo) 38 | (dotimes (x (nth 1 item)) 39 | (insert (nth 0 item))) 40 | (newline)) 41 | nil 42 | ELISP> hellohellohellohellohello 43 | byebye 44 | laterlaterlaterlaterlaterlaterlater 45 | #+END_EXAMPLE 46 | 47 | * 着色 48 | :PROPERTIES: 49 | :CUSTOM_ID: using-color 50 | :END: 51 | 52 | #+BEGIN_EXAMPLE 53 | (insert (propertize "bar" 'face '(:foreground "red"))) 54 | (insert (propertize "bar" 'face '(:background "gray30"))) 55 | (insert (propertize "bar" 'face '(:foreground "#33AAFF"))) 56 | (insert (propertize "bar" 'face '(:foreground "orange" :background "purple"))) 57 | #+END_EXAMPLE 58 | 59 | 在 =ielm= 中需要先输入 =M-x font-lock-mode=. 60 | 然后你会在屏幕底部看到一列信息 =Font-Lock mode disabled= (如果提示的是 =enabled=, 那么再输入一次 =M-x font-lock-mode= ). 61 | 62 | -------------------------------------------------------------------------------- /elisp-common/Part 6: 更简洁的绘图, if语句 以及 random函数.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Part 6: 更简洁的绘图, if语句 以及 random函数 2 | #+URL: http://dantorop.info/project/emacs-animation/lisp6.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2020年 02月 10日 星期一 10:04:03 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | * 更简洁的=drawxy=版本 10 | :PROPERTIES: 11 | :CUSTOM_ID: a-more-succinct-version-of-drawxy-from-last-week 12 | :END: 13 | 14 | 将光标移动到屏幕上的任意 =x= 列 =y= 行并绘制 =sprite=. 15 | 16 | #+begin_src emacs-lisp 17 | (defun drawxy (x y sprite) 18 | (erase-buffer) 19 | (insert-char ?\n y) 20 | (insert-char ?\s x) 21 | (insert sprite)) 22 | #+end_src 23 | 24 | 使用 =insert-char= 用于创建空格. =?\s= 是空格字符的编码。 25 | 使用 =insert-char= 可以指定要插入的字符数,并且不需要 =dotimes= 循环。 26 | 27 | * 更简洁的 =background= 版本 28 | :PROPERTIES: 29 | :CUSTOM_ID: a-more-succinct-version-of-background-from-last-week 30 | :END: 31 | 32 | #+BEGIN_EXAMPLE 33 | (defun background (width height) 34 | (erase-buffer) 35 | (dotimes (i height) 36 | (insert-char ?\. width) 37 | (newline))) 38 | #+END_EXAMPLE 39 | 40 | 使用 =insert-char= 绘制网格,保存一个 =dotimes= 循环。 41 | 这将绘制一个由句号组成的网格(通过 =?\.= 字符编码)。 42 | 可以用 =?\s= 替换 =?\.= 来画一个看不见的网格。 43 | 无论哪种方式,目标都是创建一个光标移动的空间,在不必擦除缓冲区的情况下绘制多个Sprint。 44 | 45 | * 一个简单的多的 =drawxy2= 函数 46 | :PROPERTIES: 47 | :CUSTOM_ID: the-drawxy2-function-from-last-week-a-touch-briefer 48 | :END: 49 | 50 | #+begin_src emacs-lisp 51 | (defun drawxy2 (x y sprite) 52 | (goto-char 1) 53 | (forward-line y) 54 | (forward-char x) 55 | (delete-region (point) (+ (point) (length sprite))) 56 | (insert sprite)) 57 | #+end_src 58 | 59 | * 使用 =if= 60 | :PROPERTIES: 61 | :CUSTOM_ID: using-if 62 | :END: 63 | 64 | =background= 和 =drawxy2= 需要预先执行 65 | 66 | #+begin_src emacs-lisp 67 | (defun where () 68 | (background 40 25) 69 | (dotimes (i 10) 70 | (if (= i 5) 71 | (drawxy2 30 i "here") 72 | (drawxy2 (* i 3) (* i 2) "there?")) 73 | (sit-for 0.2))) 74 | #+end_src 75 | 76 | * 使用 =random= 77 | :PROPERTIES: 78 | :CUSTOM_ID: using-random 79 | :END: 80 | 81 | =background= 和 =drawxy2= 也需要预先执行 82 | 83 | #+begin_src emacs-lisp 84 | (defun where2 () 85 | (background 40 25) 86 | (dotimes (i 20) 87 | (if (= i 12) 88 | (drawxy2 30 i (propertize "here" 'face '(:foreground "green"))) 89 | (drawxy2 (random 30) (random 25) "there?")) 90 | (sit-for 0.2))) 91 | #+end_src 92 | -------------------------------------------------------------------------------- /elisp-common/format-spec.org: -------------------------------------------------------------------------------- 1 | #+TITLE: format-spec 2 | #+URL: http://mbork.pl/2016-11-19_format-spec 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2016-12-04 日 14:40] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 上周我正在编写Emacs-y的时候,突然发现需要一个类似 =format= 的函数. 它接受一个包含有控制码(比如%t%或者%h这样的东西),然后返回的字符串中那些控制码被实际内容所填充. 10 | 要编写这么个东西也不怎么难,不过我是在不想再重现造轮子(而且要把这个函数写好也可得花一番功夫呢). 我[[https://lists.gnu.org/archive/html/help-gnu-emacs/2016-11/msg00064.html][在help-gnu-emacs的邮件列表上求助]], 结果被告知有一个 =format-spec= 函数. 11 | 该函数接受两个参数: 一个格式字符串,还有一个指代说明(它其实是一个alist用来将控制码映射成对应的值),下面是一个例子: 12 | 13 | #+BEGIN_SRC emacs-lisp 14 | (format-spec "%n %v %p %o." 15 | '((?n . "A dog") 16 | (?v . "jumps") 17 | (?p . "over") 18 | (?o . "the fence"))) 19 | (format-spec "%n %v %o." '((?n . 7) (?v . 8) (?o . 9))) 20 | #+END_SRC 21 | 22 | 值得注意的是,替代的值不一定必须是字符串 - 其他的对象也是可以的,但它们必须是可打印的,会用 =(format "%s" ...)= 来将其转换成字符串. 另外,你也可以指定任意的宽度和精度,还能指定是否左对齐(就像你在 =(format "%s" ...)= 中做的那样,这在format-spec中被称为underneath). 23 | 24 | 为了完整起见, 我还要提醒一下, 如果你要为 =format-spec= 的指代参数设置值的话,你需要用用反引号引起来,或者用 =format-spec-make= 函数创建一个指代列表. 25 | 26 | 再一次,我被Emacs的可配置性所折服了 – 我也好希望我的代码能跟Emacs的代码一样有那么强的可配置性. 27 | -------------------------------------------------------------------------------- /elisp-common/使用ERT进行elisp单元测试.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 使用 ERT 进行 elisp 单元测试 2 | #+URL: http://nullprogram.com/blog/2012/08/15/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2017-03-04 Sat 15:23] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 10 | Emacs 24 自带了一款用于单元测试的库名叫 =ERT (Emacs Lisp Regression Testing)=. 11 | 我也是在看过 [[http://emacsrocks.com/][Extending Emacs Rocks!]] 之后才知道有这个东西存在的,打那以后,我就一直在用它. 12 | 它真的很好用,以至于我甚至 [[https://github.com/skeeto/.emacs.d/commit/59d3eac73edbad8a5be72a81c7d6c5b1193bbb90][为它设定了快捷键]] 这样在任何使用我都可以方便地运行测试了. 13 | 最近在对我的 Emacs web server 进行修改时,就用 ERT 添加了一套 [[https://github.com/skeeto/emacs-http-server/blob/master/simple-httpd-test.el][测试案例]]. 14 | 15 | Emacs 自带了 ERT 的 manual,所以你可以很方便的就能学到怎么用. 16 | 它的关键自于两个宏: =ert-deftest= 以及 =should=. 17 | 第一个宏用来创建一个测试案例,而第二个宏类似于 =assert=, 但是比它更好用. 18 | 下面是一个例子, 19 | 20 | #+BEGIN_SRC emacs-lisp 21 | (ert-deftest example-test () 22 | (should (= (+ 9 2) 11))) 23 | #+END_SRC 24 | 25 | =ert-deftest= 跟其他的 =def*= 一样. 它的参数列表目前没有什么实际用途,只是让它看起来更像 =defn=而已,所以为空. 26 | 它的 body 则跟 =defun= 中的 body 一样. 最终它会生成一个匿名函数,并把这个匿名函数放入符号 example-test 的 plist 中. 27 | 当要运行测试案例时,ERT 通过搜索所有 internd 符号的 plist 来找出测试案例. 28 | 29 | 而另一个宏 =should=, 会接受一个 form 作为参数,并检查这个 form 的运行结果是否为真. 30 | 类似的宏还有 =should-not= 和 =should-error=. 31 | 32 | 执行 =M-x ert= 开始进行测试. ERT 首先让你选择要测试哪些案例. 输入 =t= 表示测试所有的案例. 33 | 除此之外你也可以选择只测试部分案例(:new, :passed, :failed 等). 34 | 我的话一般直接就直接测试所有案例得了. 然后 ERT 会弹出一个 buffer 用来显示测试的结果,按 =q= 可以推出该 buffer. 35 | 36 | * Running ERT 37 | 38 | =should= 的特别之处在于它会报告造成测试失败的语句及其返回值. 比如,若我将上一个测试案例修改为这样 39 | 40 | #+BEGIN_SRC emacs-lisp 41 | (ert-deftest example-test () 42 | (should (= (+ 9 2) 100))) 43 | #+END_SRC 44 | 45 | 那么该案例就会测试失败,并显示如下信息. 46 | 47 | #+BEGIN_SRC ert-results 48 | F example-test 49 | (ert-test-failed 50 | ((should 51 | (= 52 | (+ 9 2) 53 | 100)) 54 | :form 55 | (= 11 100) 56 | :value nil)) 57 | #+END_SRC 58 | 59 | 里面显示出了我们进行比较的语句 _(+ 9 2)_ 和 _100_ 以及他们的执行结果: (= 11 100). 60 | 把光标放到测试结果中,然后按下 =.= 就会跳转到案例的定义处了,然后你就可以进行更进一步的检查了. 61 | 你也可以按下 =b= 可以查看 backtrace, 按下=m= 可以查看案例测试过程中产生的所有输出信息, 或者按 =r= 重新运行测试. 62 | 63 | * Mocking 64 | 65 | Elisp 动态绑定的能力使得我们可以很方便地用仿真函数代替实际函数进行测试. 66 | 比如,假设我有一个函数需要检查某个特定的文件是否存在. 在函数内部我当然会使用 =file-exists-p= 来作检查. 67 | 但是这意味着你在测试前需要往文件系统中创建或删除文件. 这样子的单元测试当使用并行的方式进行测试时就会出现问题. 68 | 69 | 不过我还可以在 flet 语句中暂时用仿真函数重载 =file-exists-p= 的函数定义. 70 | 注意到 =file-exists-p= 其实是用 C 写的函数,但是依然能够被重载掉,这跟普通的 lisp 函数没什么两样. 71 | 72 | #+BEGIN_SRC emacs-lisp 73 | (defun determine-next-action () 74 | (if (file-exists-p "death-star-plans.org") 75 | 'bring-him-the-passengers 76 | 'tear-this-ship-apart)) 77 | 78 | (ert-deftest file-check-test () 79 | (flet ((file-exists-p (file) t)) 80 | (should (eq (determine-next-action) 'bring-him-the-passengers))) 81 | (flet ((file-exists-p (file) nil)) 82 | (should (eq (determine-next-action) 'tear-this-ship-apart)))) 83 | #+END_SRC 84 | 85 | 这只是一个很间的 mock 例子. 在实际的单元测试中,我可能回让这个仿真函数根据文件名的模式而返回 t 或 nil. 86 | ERT 也确实有一个扩展包,el-mock.el,可以帮助你编写很复杂的 mock,不过到目前为止似乎我还没有遇到要用它的情况. 87 | 88 | ERT实在是太好用了,我决定以后一直用它了. 89 | -------------------------------------------------------------------------------- /elisp-common/养成使用sharp quote的习惯.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 养成使用sharp quote的习惯 2 | #+URL: https://endlessparentheses.com/get-in-the-habit-of-using-sharp-quote.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2020年 02月 19日 星期三 09:53:39 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 10 | sharp quote(又名function quote,或可以简单地用 =#'= 表示)是 [[https://doc.endlessparentheses.com/Fun/function][=function=]] 语句的缩写。 11 | 它本质上是一种支持字节码编译的 [[https://doc.endlessparentheses.com/Fun/quote][=quote=]] (也可以表示为 ='=),不过它的实际用途在这些年里已经发生了变化。 12 | 13 | 二十多年前,它被用来引用[[https://doc.endlessparentheses.com/Fun/lambda][=lambda=]]语句。你知道的, ='(lambda (x) (* x x))= 对字节编译器来说只是一个单纯的列表,但 =#'(lambda (x) (* x x))= 是一个可以编译的实际函数。 14 | 如今-或者更确切地说,近十年里,lambda语句生成的就是sharp-quote引起来的自己,也就是是一个普通的 =(lambda (x) (* x x))= 与它的 =#'= 版本 [[http://www.gnu.org/software/emacs/manual/html_node/elisp/Anonymous-Functions.html][是相同的]]。事实上,你 /永远不应该/ 用任何一种引号引用你的lambdas。 15 | 16 | 另一方面,你可能正觉得shartp quote对elisp程序员来说是多余的,结果它又有了新的用途。 17 | 当编译器注意到你使用了一个未定义的函数时,它就会抛出一个警告,比如 =(not-defined “oops”)=, 但是对于像 =(mapcar 'not-defined some-list)= 这样来调用的函数,它却不会这样做,因为它不知道这个符号是一个函数的名称。 18 | sharp quote是向编译器传递信息的一种方式,因此如果它遇到 =(mapcar #'not-defined some-list)=,就会抛出相应的警告。 19 | 20 | 因此,对指代函数名每个符号使用sharp quote是一种很好的做法,无论它是在 =mapcar=, =apply=, =funcall= 还是其他任何地方。 21 | 坚持这样做还让我在一个包中发现了一个小错误。 22 | 23 | 当然,我们可以让加上sharp quote这件事变得更方便一些。 24 | 25 | #+begin_src emacs-lisp 26 | (defun endless/sharp () 27 | "Insert #' unless in a string or comment." 28 | (interactive) 29 | (call-interactively #'self-insert-command) 30 | (let ((ppss (syntax-ppss))) 31 | (unless (or (elt ppss 3) 32 | (elt ppss 4) 33 | (eq (char-after) ?')) 34 | (insert "'")))) 35 | 36 | (define-key emacs-lisp-mode-map "#" #'endless/sharp) 37 | #+end_src 38 | -------------------------------------------------------------------------------- /elisp-common/在Elisp中退出当前调用栈并执行另一个函数的方法.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Elisp中退出当前调用栈并执行另一个函数的方法 2 | #+URL: http://oremacs.com/2015/07/16/callback-quit/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: elisp-common 5 | #+DATE: [2016-09-28 周三 17:16] 6 | #+OPTIONS: ^:{} 7 | 8 | 今天我想要分享一段有趣的Elisp代码. 想象一下这么一段代码: 9 | 10 | #+BEGIN_SRC emacs-lisp 11 | (defun useful-command () 12 | (interactive) 13 | (do-thing-1) 14 | (do-thing-2 (funcall callback-function)) 15 | (do-thing-3)) 16 | #+END_SRC 17 | 18 | 有时候会有这种需求, 你在执行 =callback-function= 的时间点上,想跳出正在执行的这个函数(本例中指的是那个 =useful-command=),在保持当前环境的情况下转而调用另一个函数. 19 | 20 | 我是这样来实现这个需求的: 21 | 22 | #+BEGIN_SRC emacs-lisp 23 | (defmacro ivy-quit-and-run (&rest body) 24 | "Quit the minibuffer and run BODY afterwards." 25 | `(progn 26 | (put 'quit 'error-message "") 27 | (run-at-time nil nil 28 | (lambda () 29 | (put 'quit 'error-message "Quit") 30 | ,@body)) 31 | (minibuffer-keyboard-quit))) 32 | #+END_SRC 33 | 34 | 让我们仔细分析一下: 35 | 36 | + =minibuffer-keyboard-quit= 会展开调用栈,并直接退出到command loop,从而阻止了 =do-thing-3= 的执行. 这里的调用栈可以任意深,比如 =useful-command= 可能又被 =utility-command= 所调用,诸如此类. 37 | 38 | + 带nil参数的 =run-at-time= 会尽可能快的运行后面的代码,几乎就在我们回到command loop的那一刻就会运行. 39 | 40 | + 最后要留意的就是不要在minibuffer上显示Quit信息. 41 | 42 | * Sample application 43 | 44 | 假设我在ivy-mode激活的情况下调用了 =find-file= 函数. 一般情况下, 我会选择一个文件,然后按下 =C-m= 打开这个文件. 然而, 有时候我只是想用 =dired= 看一下这个文件而不是想打开这个文件. 45 | 下面这段代码来至于[[http://oremacs.com/2015/07/09/counsel-rhythmbox/][ivy multi-action interface]], 它是 =ivy-minibuffer-map= 中绑定的一个命令: 46 | 47 | #+BEGIN_SRC emacs-lisp 48 | (define-key ivy-minibuffer-map (kbd "C-:") 'ivy-dired) 49 | 50 | (defun ivy-dired () 51 | (interactive) 52 | (if ivy--directory 53 | (ivy-quit-and-run 54 | (dired ivy--directory) 55 | (when (re-search-forward 56 | (regexp-quote 57 | (substring ivy--current 0 -1)) nil t) 58 | (goto-char (match-beginning 0)))) 59 | (user-error 60 | "Not completing files currently"))) 61 | #+END_SRC 62 | 63 | 那么,在 =C-:= 被按下的那一刻,调用栈是这样子的: is pressed, the call stack is: 64 | 65 | + =C-x C-f= 调用 =find-file= 函数. 66 | + =find-file= 调用 =completion-read-function= 函数,不过实际上调用的是 =ivy-completing-read= 函数. 67 | + =ivy-completing-read= 调用了 =ivy-read= 函数. 68 | + =ivy-read= 调用了 =read-from-minibuffer=. 69 | 70 | 宏 =ivy-quit-and-run= 让我能够展开所有的调用栈,确保从 =read-from-minibuffer= 退出后,不会再运行剩下的代码. 71 | 换句话说,Emacs不会打开该文件,反而会打开一个dired buffer,并选中这个文件. 72 | 73 | 我上面所描述的场景应该蛮普遍的, 你可以用类似的方法改造 =helm=, =avy=,以及 =projectile=. 基本上,所有包含某种形式的补全(例如那些会等待输入的命令)并提供可定制keymap的东西,都能够应用这种方法. 74 | 美中不足的是: 这种方法只是一种 quick-and-dirty 的解决方案. 如果可以的话,我并不推荐使用这种方法. 不过如果你别无他法,那么这个宏也许能够帮到你. 75 | -------------------------------------------------------------------------------- /elisp-common/如何cancel所有运行某个函数的timer.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 如何cancel所有运行某个函数的timer 2 | #+URL: http://pragmaticemacs.com/emacs/cancel-all-timers-calling-some-function/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-document 5 | #+DATE: [2016-11-24 四 20:30] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 10 | 这是一篇很短的博文. 最近我发现有时会不小心启动了重复的timer. 由于我没有将这些timer对象与符号绑定,因此我无法用 =cancel-timer= 关闭这些timer. 11 | 经过一番搜索,我发现了一个名叫 =cancel-function-timers= 的函数,可以cancel掉所有运行指定函数的timer. 12 | 13 | 这个函数你不会经常用到,但是保不齐什么时候就有用呢! 14 | -------------------------------------------------------------------------------- /elisp-common/如何让重复调用emacs函数尽可能的方便.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 如何让重复调用emacs函数尽可能的方便 2 | #+URL: http://zck.me/emacs-repeat-emacs-repeat 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: elisp-common 5 | #+DATE: [2016-10-07 五 21:20] 6 | #+OPTIONS: ^:{} 7 | 8 | 有时我们会需要重复执行一些由快捷键出发的Emacs函数. 这里有一种很简单的实现方式. 假设有一个简单的函数它的功能就是插入"here": 9 | 10 | #+BEGIN_SRC emacs-lisp 11 | (defun insert-here () 12 | (interactive) 13 | (insert "here")) 14 | #+END_SRC 15 | 16 | 它的快捷键为 =C-c h h=: 17 | 18 | #+BEGIN_SRC emacs-lisp 19 | (global-set-key (kbd "C-c h h") #'insert-here) 20 | #+END_SRC 21 | 22 | 好了; 现在每当我们按下 =C-c h h= 就会在当前buffer插入"here"了. 但是我们经常会需要重复执行该命令. 23 | 诚然,我们可以通过 =C-x z= 来重复执行上一条命令,但是这需要我们按两下键盘呢,而且也不是特别的方便. 24 | 不过,我们可以这样做, 先创建一个[[https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Keymaps.html#index-make_002dsparse_002dkeymap][sparse keymap]] 然后再这个keymap中设置 =h= 为该命令的快捷键. 25 | 26 | #+BEGIN_SRC emacs-lisp 27 | (setq insert-here-keymap 28 | (let ((map (make-sparse-keymap))) 29 | (define-key map (kbd "h") #'insert-here) 30 | map)) 31 | #+END_SRC 32 | 33 | 现在我们只需要在 =#'insert-here= 中设置该keymap就行了. 我们不能用 =#'use-local-map= 来设置,因为这样一来我们就无法再插入字母h了:我们总是会优先使用 =#'use-local-map= 中的键绑定. 相反,我们需要用 =#'set-transient-map= 来设置,它会临时使用局部keymap,但在按下一个快捷键后就失效了. 34 | 35 | #+BEGIN_SRC emacs-lisp 36 | (defun insert-here () 37 | (interactive) 38 | (insert "here") 39 | (set-transient-map 40 | insert-here-keymap)) 41 | #+END_SRC 42 | 43 | 现在运行 =#'insert-here= 后, 我们可以通过按下 =h= 来重复执行该函数. 但若按下了其他的快捷键, 则该keymap会被禁用. 然后再按下 =h= 就会插入字母h了. 44 | 这样我们只需要按下一个键就能重复执行该命令了,很简单吧. 45 | -------------------------------------------------------------------------------- /elisp-common/确定Emacs server是否启动, 或者说server-running-p的迷思.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 确定Emacs server是否启动, 或者说server-running-p的迷思 2 | #+URL: http://emacshorrors.com/posts/determining-if-the-server-is-started-or-the-wonders-of-server-running-p.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2018年 07月 05日 星期四 17:12:02 CST] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 一个小问题:如何确定当前Emacs实例是否启动了服务器? 10 | 11 | 快速搜索一下,我们可以得到三种方法: =server-mode=, =(daemonp)= 和 =(server-running-p)=. 12 | 这太多了,但是其中一个肯定是正确答案对吗? 然而, 并不是. 因为这个小问题的真正答案是:你无能为力。 13 | 14 | - 且仅当服务器使用 =server-mode= 函数启动时 =server-mode= 的值才为 =t=。但是我们还有其他方法可以启动服务器,比如 =M-x server-start= 或 =emacs --daemon= 。 15 | - 且仅当Emacs在守护进程模式下启动时, =(daemonp)= 才会返回 =t=. 16 | 17 | 那么 =(server-run-p)= 呢?它看起来可能很不错,但是也有坑. 18 | 19 | 它初看起来很不错:执行 =M-x server-start= 后, =(server-running-p)= 会返回 =t=! 20 | 那么我们是不是就可以选它了呢? 还不行! 21 | 让我们启动一个新的Emacs实例,在不启动服务器的情况下执行 =(server-run-p)= 会发现依然返回 =t=! 22 | 23 | 怎么回事?事实上 =(server-running-p)= 有点名不符实. 以下是其完整的源代码: 24 | 25 | #+begin_src emacs-lisp 26 | (defun server-running-p (&optional name) 27 | "Test whether server NAME is running. 28 | 29 | Return values: 30 | nil the server is definitely not running. 31 | t the server seems to be running. 32 | something else we cannot determine whether it's running without using 33 | commands which may have to wait for a long time." 34 | (unless name (setq name server-name)) 35 | (condition-case nil 36 | (if server-use-tcp 37 | (with-temp-buffer 38 | (insert-file-contents-literally (expand-file-name name server-auth-dir)) 39 | (or (and (looking-at "127\.0\.0\.1:[0-9]+ \([0-9]+\)") 40 | (assq 'comm 41 | (process-attributes 42 | (string-to-number (match-string 1)))) 43 | t) 44 | :other)) 45 | (delete-process 46 | (make-network-process 47 | :name "server-client-test" :family 'local :server nil :noquery t 48 | :service (expand-file-name name server-socket-dir))) 49 | t) 50 | (file-error nil))) 51 | #+end_src 52 | 53 | doc-string中描述了恐怖的细节。函数中的 =-p= 后缀说明这是一个谓词,即布尔函数。 54 | 但在 =server-running-p=, 返回 非 =nil= 值并不一定就是响亮而明确的 “是!”, 它的意思其实是“嗯,也许吧,谁知道呢?” 55 | 这是第三种可能,因为Emacs并不是黑白分明的。 56 | 57 | 但是这个函数做什么呢?它试图确定一个名叫 =NAME= 的服务器是否正在运行,方法是假设该服务器的配置与正在运行的实例完全一致。 58 | 它可能最终会查看当前服务器的套接字文件,或者尝试启动一个TCP连接(这个开销非常昂贵)。 59 | 你可能会在构建mode line时调用 =server-running-p= 函数: 但是它会让你立刻冻结Emacs运行而且还无法恢复. 60 | 它的使用场景目前还不太清楚. 它无法确定正在运行的实例是否启用了服务器 --- 但是它会使用该服务器的配置去搜索潜在的完全不同的其他服务器. 61 | 62 | -------------------------------------------------------------------------------- /elisp-common/聊一聊Emacs的字节编译.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 聊一聊Emacs的字节编译 2 | #+URL: http://nullprogram.com/blog/2010/07/01/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2017-03-02 周四 11:00] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 某些Lisp有一项独门绝迹,那就是可以在任意时刻将函数编译成字节码/本地代码(具体编译成什么有赖于方言和具体实现). 10 | 对于那些能够编译的Lisp函数(例如[[http://clisp.cons.org/][CLISP]]), 一般都支持两种形式的代码: 一种是缓慢的,未优化未编译过的代码,一种是快速,高效编译过的代码. 11 | 但是你不要以为未编译的代码就一无是处了,除了无需花费时间在编译上之外,它也有其他的有点. 12 | 13 | 对于Emacs Lisp来说, 未编译过的函数就是一个lambda表达式. 只不过它存在于一个symbol中,就相当与给它取了个名字. 14 | 而编译后的form则是一个(特殊的)数组, 编译后的字节码以字符串的形式存在这个数组的第二个元素中. 其他的元素还包括常量,docstring等. 15 | Elisp提供了 =byte-compile= 函数来编译其他函数. 该函数能接受一个lambda函数或者一个symbol. 16 | 不过如果参数是symbol的话,编译后的函数回覆盖该symbol的原S表达式. 17 | 18 | #+BEGIN_SRC emacs-lisp 19 | (byte-compile (lambda (x) (* 2 x))) 20 | => #[(x) "^H\301_\207" [x 2] 2] 21 | #+END_SRC 22 | 23 | 编译器不仅仅是将函数是转换成字节码并展开宏,还会进行一些优化,比如删除无用代码,预先执行 safe constant forms, 以及内联函数. 从而极大地提高执行效率. 24 | (通过我的 [[http://nullprogram.com/blog/2009/05/28/][measure-time macro]] 来测算的) 25 | 26 | #+BEGIN_SRC emacs-lisp 27 | (defun fib (n) 28 | "Fibonacci sequence." 29 | (if (<= n 2) 1 30 | (+ (fib (- n 1)) (fib (- n 2))))) 31 | 32 | (measure-time 33 | (fib 30)) 34 | => 1.0508708953857422 35 | 36 | (byte-compile 'fib) 37 | 38 | (measure-time 39 | (fib 30)) 40 | => 0.4302399158477783 41 | #+END_SRC 42 | 43 | 大多数Emacs自带的函数都预先编译过了. 不过也有部分自带的函数没有被编译,所以我想,不如我来花点时间把它们编译一下算了. 44 | 45 | Common Lisp是有提供一个判断函数(compiled-function-p)来测试另一个函数是否有编译过的. 46 | 但不知为何,Elisp中没有预定义这样一个函数,我只能自己写一个了: 47 | 48 | #+BEGIN_SRC emacs-lisp 49 | (defun byte-compiled-p (func) 50 | "Return t if function is byte compiled." 51 | (cond 52 | ((symbolp func) (byte-compiled-p (symbol-function func))) 53 | ((functionp func) (not (sequencep func))) 54 | (t nil))) 55 | #+END_SRC 56 | 57 | 我一开始的想法是便利所有interned symbol,若通过上面的测试函数发现该symbol的function slot中是未编译的函数,那么就调用 =byte-compile= 来编译它. 58 | 不过后来我发现, =byte-compile= 足够聪明,它会自动忽略那些没有函数以及那些函数以及编译过的symbol. 59 | 60 | 那么,下一个问题就是,我们如何遍历每个interned symbol? 函数 =mapatoms= 可以做到这一点. 给它一个函数作为参数,它就会将该函数应用到每个interned symbol上. 61 | 这样一来,问题就简单了. (译者注:我运行了这个语句之后,Evil彻底用不了了,不得不重启Emacs...) 62 | 63 | #+BEGIN_SRC emacs-lisp 64 | (mapatoms 'byte-compile) 65 | #+END_SRC 66 | 67 | 这就搞定了! 整个过程不过几秒钟,不过会产生大量的警告. 目前我还未找到屏蔽这些警告方法,所以你最好不要自动运行这个语句,否则你会发现时不时地会弹出一个window. 68 | 而且,我也不确定这么做会不会损坏你的Emacs session,毕竟不是所有的函数写的时候都有好好的考虑编译后还能不能用,尤其是那些用到宏的函数. 69 | 70 | 另外,我很怀疑这样能否带来性能上的明显提升. 就像我前面所得,大多数的函数预先编译过了,而这部分函数才是使用最频繁的函数. 71 | 把所有函数都编译一次,可能只是给你你心理上安慰一点而已. 72 | -------------------------------------------------------------------------------- /elisp-common/让Emacs shell命令发送桌面通知.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 让Emacs shell命令发送桌面通知 2 | #+URL: https://blog.hoetzel.info/post/eshell-notifications/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2019-04-27 六 22:12] 6 | #+LANGUAGE: zh-CN 7 | #+STARTUP: inlineimages 8 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 9 | 10 | 我总是使用 [[https://www.gnu.org/software/emacs/manual/html_mono/eshell.html][Eshell]] 来与操作系统进行交互,因为它与Emacs无缝整合、支持处理 (远程) [[https://www.gnu.org/software/tramp/][TRAMP]] 文件 而且在Windows上也能工作得很好. 11 | 12 | 启动 shell 命令后 (比如耗时严重的构建任务) 我经常会由于切换buffer而忘了追踪任务的运行状态. 13 | 14 | 多亏了 Emacs 的 [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Hooks.html][hooks]] 机制,你可以配置 Emacs 在某个外部命令完成后调用一个elisp函数. 15 | 16 | 我使用 [[https://github.com/jwiegley][John Wiegleys]] 所编写的超棒的 [[https://github.com/jwiegley/alert][alert]] 包来发送桌面通知: 17 | 18 | #+begin_src emacs-lisp 19 | (require 'alert) 20 | 21 | (defun eshell-command-alert (process status) 22 | "Send `alert' with severity based on STATUS when PROCESS finished." 23 | (let* ((cmd (process-command process)) 24 | (buffer (process-buffer process)) 25 | (msg (format "%s: %s" (mapconcat 'identity cmd " ") status))) 26 | (if (string-prefix-p "finished" status) 27 | (alert msg :buffer buffer :severity 'normal) 28 | (alert msg :buffer buffer :severity 'urgent)))) 29 | 30 | (add-hook 'eshell-kill-hook #'eshell-command-alert) 31 | #+end_src 32 | 33 | [[https://github.com/jwiegley/alert][alert]] 的规则可以用程序来设置. 就我这个情况来看,我只需要当对应的buffer不可见时被通知: 34 | 35 | #+begin_src shell 36 | (alert-add-rule :status '(buried) ;only send alert when buffer not visible 37 | :mode 'eshell-mode 38 | :style 'notifications) 39 | #+end_src 40 | 41 | 这甚至对于 [[https://www.gnu.org/software/tramp/][TRAMP]] 也一样生效. 下面这个截屏展示了失败的 =make= 命令产生的 Gnome 桌面通知. 42 | 43 | [[https://blog.hoetzel.info/img/eshell.png]] 44 | -------------------------------------------------------------------------------- /elisp-common/重设defvar定义的值.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 重设defvar定义的值 2 | #+URL: http://irreal.org/blog/?p=5929 3 | #+AUTHOR: lujun9972 4 | #+TAGS: elisp-common 5 | #+DATE: [2017-02-27 Mon 21:38] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 关于 [[http://irreal.org/blog/?p=5926][昨天的博文]], 我后来在 [[https://www.reddit.com/r/emacs/comments/5tkhe3/what_is_the_best_workflow_to_contributing_to/][Reddit上看到这个问题]] 就又学到一招. Elisp和大多数的Lisp有一个很反直觉的特性就是由 =defvar= 和 =defcustom= 定义的值是不可变的: 一旦设置了值就只能重启进程才能改变. 10 | 11 | 后来发现,如果你用 =Ctrl-Meta-x= 而不是 =Ctrl-x Ctrl-e=来执行 12 | 13 | #+BEGIN_SRC emacs-lisp 14 | (defvar user "Jim") 15 | #+END_SRC 16 | 17 | 那么你依然能够重置变量值. 这在开发阶段需要调整参数时,非常有帮助. 18 | 虽说Emacs普通用户无需知道这个,而且即使你开发Elisp,也可能不会经常用到这个技巧,不过应用这个技巧还是蛮方便的. 19 | 同时这个经历也更加证明了,关于Emacs的学习,学无止境. 20 | -------------------------------------------------------------------------------- /emacs-common/2个鲜为人知的提高Emacs启动速度的步骤.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 2个鲜为人知的提高Emacs启动速度的步骤 2 | #+URL: https://www.reddit.com/r/emacs/comments/3kqt6e/2_easy_little_known_steps_to_speed_up_emacs_start/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-10-10 一 21:25] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | * 1: 在初始化文件的头部添加(set gc-cons-threshold 100000000) 10 | 11 | =gc-cons-threshold= 指定了垃圾回收前允许有多少字节的consing. 这个值一般是800000bytes, 但是这会使得我在Emacs启动期间调用多达39次的垃圾回收!!!,而垃圾回收的过程又巨慢无比. 12 | 我有16G的内存,因此我把这个值设置成了大约100MB. 现在我的Emacs在启动期间根本不会发生垃圾回收过程,这使得我的启动时间加快了600毫秒. 13 | 14 | * 2: 将你的配置文件嵌入(let ((file-name-handler-alist nil)) )中 15 | 16 | =file-name-handler-alist= 的默认值为 17 | 18 | #+BEGIN_SRC emacs-lisp 19 | (("\\`/[^/]*\\'" . tramp-completion-file-name-handler) 20 | ("\\`/[^/|:][^/|]*:" . tramp-file-name-handler) 21 | ("\\`/:" . file-name-non-special)) 22 | #+END_SRC 23 | 24 | 也就是说在Emacs在启动过程中加载el和elc文件时都会将其文件名与这些正则表达式进行匹配. 25 | 26 | 这使得我的启动时间又快了200毫秒. 27 | 28 | * 3: 如果你愿意重现编译Emacs的话,你还可以这样做 29 | 30 | 应用这个[[https://gist.github.com/bsuh/e7cba8a61f482b8d8687][补丁]] 31 | 32 | Emacs在加载el文件(也许加载elc文件时也是这样)时使用的,会用 =getc= 函数从FILE中读取字符. 33 | =getc= 由于使用了锁机制因而是线程安全的. 然而在单线程中循环调用 =getc= 就显得没有必要了. 34 | 我将 =getc= 替换成 =getc_unlocked= 之后,启动速度又提升了200毫秒. 35 | 36 | 整体来说,我的启动速度从1.5秒缩短到了0.5秒! 真爽啊 :) 37 | -------------------------------------------------------------------------------- /emacs-common/Debug时显示Emacs的加载时间.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Debug时显示Emacs的加载时间 2 | #+URL: http://qsdfgh.com/articles/2016/11/02/emacs-loading-time.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2016-11-29 二 21:19] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | * The goal 10 | 11 | Gitlab/Github/Bitbucket (以及其他的VCS前端) 上可以看到大量的Emacs配置项目, 从这些项目中可以看出有一种流行的配置方法是: 在 =init.el= 文件中做一些基础性的设置,然后从指定目录中加载其他文件. 12 | 13 | 这是一种很流行的配置方式,这种方式可以实现按主题或者包来划分配置. 14 | 15 | 现在我们假设Emacs的加载速度超慢,而你希望能找出这是加载哪个文件导致的. 16 | 17 | * The prerequisites 18 | 19 | 假设被加载的配置文件存在 =/home/you/src/lisp= 中. 20 | 21 | #+BEGIN_EXAMPLE 22 | total 16 23 | -rw-r--r-- 1 you you 41B 2 nov 10:48 super-feature.el 24 | -rw-r--r-- 1 you you 41B 2 nov 10:48 extra-feature.el 25 | #+END_EXAMPLE 26 | 27 | 同时要保证你在配置文件中用了 [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Named-Features.html][provide]] 函数来提供了feature: 28 | 29 | #+BEGIN_SRC emacs-lisp 30 | ;;; super-feature.el --- My super feature 31 | 32 | ;;; Commentary: 33 | 34 | ;;; Code: 35 | 36 | (some-heavy-configuration) 37 | 38 | (provide 'super-feature) 39 | 40 | ;;; super-feature.el ends here 41 | #+END_SRC 42 | 43 | 到目前为止还没什么神奇的! 44 | 45 | Tip: 你的文件不要以数字开头,否则该文件不会被加载的. 46 | 47 | * The snippet 48 | 49 | 我们要实现的是,当使用 =--debug-init= 参数启动Emacs时,开始度量加载各文件的时间. 50 | 51 | 我们可以通过变量 [[http://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/startup.el#n1007][init-file-debug]] 来查看Emacs启动时是否带了 =--debug-init= 参数. 然后使用内建的 [[http://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/emacs-lisp/benchmark.el][benchmark]] 库来进行度量. 52 | 53 | 要做到这一点,你的 =init.el= 应该看起来像这样: 54 | 55 | #+BEGIN_SRC emacs-lisp 56 | ;; Display the total loading time in the minibuffer 57 | 58 | (defun display-startup-echo-area-message () 59 | "Display startup echo area message." 60 | (message "Initialized in %s" (emacs-init-time))) 61 | 62 | ;; Benchmark loading time file by file and display it in the *Messages* buffer 63 | 64 | (when init-file-debug 65 | (require 'benchmark)) 66 | 67 | (let ((lisp-dir "/home/you/src/lisp")) 68 | (add-to-list 'load-path lisp-dir) 69 | (mapc (lambda (fname) 70 | (let ((feat (intern (file-name-base fname)))) 71 | (if init-file-debug 72 | (message "Feature '%s' loaded in %.2fs" feat 73 | (benchmark-elapse (require feat fname))) 74 | (require feat fname)))) 75 | (directory-files lisp-dir t "\\.el"))) 76 | #+END_SRC 77 | 78 | 这样一来,当你用 =emacs --debug-init= 启动Emacs时,就会在 =*Messages*= buffer中看到有详细的加载时间了. 79 | 80 | 这段代码只有在启动Emacs时带了 =--debug-init= 才会生效,不带该参数时就跟普通的启动过程一样了. 81 | 82 | * The result 83 | 84 | 你将会在 =*Messages*= buffer中看到类似下面的结果: 85 | 86 | #+BEGIN_EXAMPLE 87 | Feature ’extra-feature’ loaded in 0.08s 88 | Feature ’super-feature’ loaded in 2.27s 89 | #+END_EXAMPLE 90 | 91 | Happy debugging! 92 | -------------------------------------------------------------------------------- /emacs-common/Eldoc Goes Global.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Eldoc全局化了 2 | #+URL: https://emacsredux.com/blog/2018/11/13/eldoc-goes-global/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common,eldoc 5 | #+DATE: [2019年 09月 01日 星期日 19:36:13 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 最近我注意到 Emacs 25.1 增加了一个名为 =global-eldoc-mode= 的模式,它是流行的 =eldoc-mode= 的一个全局化的变体. 10 | 而且与 =eldoc-mode= 不同的是, =global-eldoc-mode= 默认是开启的! 11 | 12 | 这意味着你可以删除Emacs配置中为主模式开启 =eldoc-mode= 的代码了: 13 | 14 | #+begin_src emacs-lisp 15 | ;; That code is now redundant 16 | (add-hook 'emacs-lisp-mode-hook #'eldoc-mode) 17 | (add-hook 'ielm-mode-hook #'eldoc-mode) 18 | (add-hook 'cider-mode-hook #'eldoc-mode) 19 | (add-hook 'cider-repl-mode-hook #'eldoc-mode) 20 | #+end_src 21 | 22 | [[https://emacs.stackexchange.com/questions/31414/how-to-globally-disable-eldoc][有人说]] =global-eldoc-mode= 在某些不支持的模式中会有性能问题. 我自己重未遇到过, 但若你像禁用它则只需要这样: 23 | 24 | #+begin_src emacs-lisp 25 | (global-eldoc-mode -1) 26 | #+end_src 27 | 28 | 现在是时候清理我的配置了! 删除代码就是这么爽! 29 | -------------------------------------------------------------------------------- /emacs-common/Emacs Swiper 以及 multiple cursors.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Emacs swiper 以及 multiple cursors 2 | #+URL: http://xenodium.com/emacs-swiper-and-multiple-cursors/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2019年 12月 12日 星期四 14:07:36 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | Emacs [[https://github.com/abo-abo/swiper][swiper]] 棒极了. 我将 =swiper-isearch= 的快捷键设置为了 =C-s=. 10 | 我同时也是 [[https://github.com/magnars/multiple-cursors.el][multiple cursors]] 的大粉丝. 我经常用它(挺有趣的). 11 | 12 | 我完全错过了 Ole 早在 2015 年写的文章: [[https://oremacs.com/2015/10/14/swiper-mc/][一个基于swiper的multiple cursor扩展]]. 原来, swiper 自带 multiple cursors 支持 (默认快捷键为 =C-7= ). 耶! 13 | 14 | 更新: 感谢 [[https://irreal.org/blog/?p=8370][irreal 的这篇文章]], 请一定记得将 =swiper-mc= 添加到 =mc/cmds-to-run-once= 列表中 (否者结果将出人意料). 当你第一次调用 =C-7= 时会有提示. 15 | 当你看到类似下面的提示时请一定选择 "n": 16 | 17 | [[http://xenodium.com/images/emacs-swiper-and-multiple-cursors/swiper-mc-no.png]] 18 | 19 | 若你不小心错选了 "y", 那么可以在 *~/.emacs.d/.mc-lists.el* 中修正它. 将 =swiper-mc= 从 =mc/cmds-to-run-for-all= 中删除掉, 20 | 同时将其添加到 =mc/cmds-to-run-once= 中. 调用 =m-x eval-buffer= 来重置这些变量就搞定了. 21 | 22 | [[http://xenodium.com/images/emacs-swiper-and-multiple-cursors/swipermc.gif]] 23 | -------------------------------------------------------------------------------- /emacs-common/EmacsWiki- Programmable Completion.org: -------------------------------------------------------------------------------- 1 | #+TITLE: EmacsWiki: Programmable Completion 2 | #+URL: https://www.emacswiki.org/emacs/ProgrammableCompletion 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common/ 5 | #+DATE: [2017-04-17 一 12:58] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 文本会讲解的Emacs标准库中的 `pcomplete.el’ 及其相关库. 要看`pcomplete.el’的相关例子,请移步 [[https://www.emacswiki.org/emacs/PcompleteExamples][PcompleteExamples]]. 10 | 11 | *注意:* `pcomplete.el’的意思是“programmable completion”,它与 [[https://www.emacswiki.org/emacs/EmacsLisp][EmacsLisp]] manual ([[http://www.emacswiki.org/cgi-bin/info-ref?find=dynamic-completion-table][dynamic-completion-table]]) 中所说的“programmed completion”没有任何关系. 12 | 后者的意思是使用函数来生成补全后选项而不是直接提供一个明确的后选项列表. 13 | 14 | * Adding programmable completion using pcomplete 15 | 16 | ** 编写setup函数,并将之加到你的mode hook中 17 | 18 | 下面是个例子: 19 | 20 | #+BEGIN_SRC emacs-lisp 21 | (defun pcomplete-erc-setup () 22 | "Setup erc-mode to use pcomplete." 23 | (set (make-local-variable 'pcomplete-parse-arguments-function) 24 | 'pcomplete-parse-erc-arguments) 25 | (set (make-local-variable 'pcomplete-command-completion-function) 26 | 'pcomplete/erc-mode/complete-command) 27 | (set (make-local-variable 'pcomplete-command-name-function) 28 | 'pcomplete-erc-command-name) 29 | (set (make-local-variable 'pcomplete-default-completion-function) 30 | (lambda () (pcomplete-here (pcomplete-erc-nicks)))) 31 | (set (make-local-variable 'pcomplete-suffix-list) '(? ?:)) 32 | ) 33 | (add-hook 'erc-mode-hook 'pcomplete-erc-setup) 34 | #+END_SRC 35 | 36 | ** 补齐你需要的那些函数 37 | 38 | + pcomplete-parse-arguments-function 39 | 40 | =pcomplete-parse-arguments-function= 不接参数. 它应该返回当前命令的参数列表. 41 | 42 | + pcomplete-command-name-function 43 | 44 | 返回命令的规范名. 会根据该名称来查找对应的补全函数. 45 | 例如,假设该函数返回值为 “MSG” 且当前的 major mode 为 “erc-mode” 的话, pcomplete会使用 =pcomplete/erc-mode/MSG= 和 =pcomplete/MSG= 这两个函数来进行补全. 46 | 47 | + pcomplete-default-completion-function 48 | 49 | 在没有为该命令定义补全函数的情况下,会调用 =pcomplete-default-completion-function= 所指向的函数. 50 | 该函数的函数体应该是这样的 =(pcomplete-here (… list of completions …))= 51 | 52 | + pcomplete-command-completion-function 53 | 54 | 该函数的函数体应该是这样的 =(pcomplete-here (… list of completions …))= 55 | -------------------------------------------------------------------------------- /emacs-common/Emacs中的Electric-Pair-Mode.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Emacs中的Electric-Pair-Mode 2 | #+URL: http://prodissues.com/2016/10/electric-pair-mode-in-emacs.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-10-11 周二 13:10] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | 一直以来我都是用[[https://textexpander.com][TextExpander]] 来作为插入文本片段和文本扩展的工具. 其中一种经常的用法是用它来自动插入配对的字符. 例如但我输入一个双引号(")时,我几乎总要用另一个双引号(")与之配对. 10 | 11 | 然而TextExpander有以下一些缺陷: 12 | 13 | 1. 性能 – 用它来进行扩展一个文本差不多要花1秒钟的时间,这就有了明显的卡顿的感觉. 14 | 2. 若我删掉了整个pair的一部分,另一部分不会跟着被删掉. 15 | 3. 我不能用它来包裹一段文本到pair中. 比如,假设我输入了一写内容,然后想用括号将这段内容括起来,用TextExpander是做不抖啊的. 16 | 17 | 除了这些技术上的缺陷外, 我也不想继续长期的使用TextExpander了. 我对它要求[[http://www.macworld.com/article/3052440/os-x/smile-updates-textexpander-and-switches-to-subscriptions.html][捐助]] 的行为感到不满. 我的意思是,为什么要为插入文本片段这种功能而付费呢…? 18 | 19 | 事实上, Emacs本身就自带了一个 =electric-pair-mode=, 它能为你自动插入配对字符, 我决定开启这个mode. 20 | 不过默认情况下,这个mode所补全的pair一般只适合于那些编程major mode,而我需要让它在某些基于text mode的major mode(比如org,markdown或单纯的text mode)下也能用. 21 | 比如,在org中,要内嵌一段代码片段,我需要用 =~= 将代码片段括起来,但是 =~= 默认并不会自动为你插入匹配相应的匹配字符. 22 | 23 | 好在,定义要定义其他pairs也很容易,只需要修改以下 =electric-pair-pairs= 变量就行了. 24 | 25 | 下面是我的相关配置: 26 | 27 | #+BEGIN_SRC emacs-lisp 28 | (electric-pair-mode 1) 29 | (setq electric-pair-pairs '( 30 | (?\" . ?\") 31 | (?\` . ?\`) 32 | (?\( . ?\)) 33 | (?\{ . ?\}) 34 | )) 35 | #+END_SRC 36 | 37 | 若我遇到了其他pairs的话,我还会继续往里面添加内容的. 另外,我还需要研究下如果为特定的major mode设置特定的pairs. 38 | -------------------------------------------------------------------------------- /emacs-common/Emacs之威: 使用Impatient Mode实时开发网站.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Emacs之威: 使用Impatient Mode实时开发网站 2 | #+URL: https://dev.to/avocadoras/the-power-of-emacs-developing-websites-in-real-time-with-impatient-mode-33fh 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2019年 02月 11日 星期一 15:36:45 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 你在编辑HTML或CSS文件是是否希望看浏览器呈现实时变化?Emacs为这个问题提供了一个简单的解决方案,假设你通过 =apt-get install Emacs=, =pacman -Syu Emacs= 或 =brew cask install Emacs= 安装好了Emacs. 9 | 设置Emacs之后,你需要通过MELPA安装 =impatient-mode= 包。我会给你留下一个链接,链接到我的[[https://github.com/avocadoras/.emacs.d.git][emacs配置]],这样你就可以在不了解emacs或如何使用MELPA设置包的情况下继续学习本教程。 10 | 11 | * 安装emacs 12 | :PROPERTIES: 13 | :CUSTOM_ID: install-emacs 14 | :END: 15 | 16 | 这些步骤应该适用于任何运行Emacs的平台。Emacs预装在许多*nix发行版中,但是您可能需要首先从系统的软件包仓库库中安装Emacs GUI。 17 | 18 | - 开启终端 19 | - 在命令提示符中输入 =$ brew cask install emacs=, =$ pacman - syu emacs= 或 =$ apt-get install emacs= 20 | 21 | * 在Emacs中安装impatient mode 22 | :PROPERTIES: 23 | :CUSTOM_ID: install-impatient-mode-in-emacs 24 | :END: 25 | 26 | 此步骤要求你为Emacs设置MELPA,并在 =.emacs.d/= 目录中的 =initel= 文件中进行配置,然后安装 =impatient-mode= Emacs包。 27 | 好在,你可以直接将我的emacs配置复制到你的 =.emacs.d= 目录中,就不需要搞这些麻烦事了。 28 | 29 | - 键入 =cd ~= 确保进入 =$HOME= 目录 30 | - 运行 =$ git clone https://github.com/avocadoras/.emacs.d.git= 31 | 32 | 克隆完后, =cd= 进入存储库。 33 | 34 | * 可选:切换到web-impatient分支 35 | :PROPERTIES: 36 | :CUSTOM_ID: optional-switch-to-web-impatient-branch 37 | :END: 38 | 39 | 我的个人配置包含许多会减慢Emacs启动的包。 40 | 如果您从头开始Emacs,您将需要在Emacs中设置MELPA,然后安装Impatient Mode。 41 | 为了减少麻烦,您可以复制我的emacs配置并切换到 =web-impatient= 这个分支,该分支比我的个人配置更快Emacs。 42 | 43 | - 在终端中提示符中输入 =$ cd .emacs.d/=, 使你进入 =.emacs.d/= 中 44 | - 输入 =$ git checkout web-impatient= 切换到 web-impatient 分支 45 | 46 | * 使用Emacs打开HTML文件 47 | :PROPERTIES: 48 | :CUSTOM_ID: tell-emacs-to-open-any-html-file 49 | :END: 50 | 51 | 您需要使用Emacs打开文件,因此切换到文件系统中包含HTML文件的目录,或者通过键入 =$ Emacs test.html= 创建一个新文件 52 | 53 | * 启动http服务器并为文件启动 impatient mode 54 | :PROPERTIES: 55 | :CUSTOM_ID: start-up-a-http-server-and-run-impatient-mode-in-your-file 56 | :END: 57 | 58 | 在打开文件后输入 =ALT-x httpd-start= (在macOS中这应该是 =OPTION-x httpd-start= )来启动与 =impatient-mode= package自带的http服务器,然后按下键 =RETURN= 或 =ENTER=. 59 | 60 | * 浏览localhost:8080/imp/test.html,然后在Emacs中输入html文件内容 61 | :PROPERTIES: 62 | :CUSTOM_ID: browse-localhost8080imptest.html-and-type-your-html-file-in-emacs 63 | :END: 64 | 65 | 启动浏览器并转到提供文件的本地地址。地址应该是 =http://localhost:8080/imp=. 66 | 您会看到一个已服务的文件列表。单击要呈现的文件。最后,在 =test.html= 中键入任何内容,您的浏览器将开始呈现您键入的所有内容。 67 | 68 | * 演示 69 | :PROPERTIES: 70 | :CUSTOM_ID: demo 71 | :END: 72 | 73 | [[https://youtu.be/YSU4FF5gTiY]] 74 | -------------------------------------------------------------------------------- /emacs-common/Emacs停止响应或崩溃了该怎么办.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Emacs停止响应或崩溃了该怎么办 2 | #+URL: https://yoo2080.wordpress.com/2013/10/03/what-to-do-when-emacs-hangs-freezes-or-crashes 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-08-08 周一 11:07] 6 | #+OPTIONS: ^:{} 7 | 8 | * Emacs停止响应了怎么办 9 | 10 | Emacs停止响应,通常是因为陷入循环中了,这时你需要按下 =C-g= 来挑出死循环. 11 | 若 =C-g= 不起作用则很可能是因为你用同步的方式调用了外部程序,而该外部程序由于某些原因一直卡着没有退出. 这种情况下,你只需要杀掉这个外部进程就可以了. 在MS Windows中你可以通过任务管理器来杀掉进程. 12 | 如果我不知道要杀掉哪个外部进程怎么办? 那你恐怕只能杀掉Emacs本身了. 但是杀掉Emacs进程会不会丢失那些还未来得及保存的工作呢? 也不尽然,我们稍候再来讨论这个问题. 13 | 14 | 在MS Windows上, Emacs可能会调用某些依赖外部工具(像grep,find,ftp,diff,latex之类的额)的命令,而这些外部工具若没有安装或者配置好的情况下,Emacs也可能会停止响应. 15 | 16 | ** 练习 17 | 18 | 下面是一段练习用的代码,执行这段代码Emacs就会停止响应了. 19 | 20 | #+BEGIN_SRC emacs-lisp 21 | (while t 22 | (print "haha")) 23 | #+END_SRC 24 | 25 | 你可以通过按下 =C-g= 退出循环. 26 | 27 | 下面这段代码只适用于MS Windows环境. 执行这段代码Emacs又会卡住了. 28 | 29 | #+BEGIN_SRC emacs-lisp 30 | (call-process "notepad") 31 | #+END_SRC 32 | 33 | 若你没有看到记事本的窗口出现的话,那么打开任务管理器找到并杀掉notepad.exe吧. 34 | 35 | * 调试 36 | 37 | 若你想要调试并找到引起Emacs停止响应的原因的话,调用命令 =toggle-debug-on-quit=. 38 | 39 | 若你想调试那些会引起Emacs停止响应且 =C-g= 不起作用的命令的话, 执行 =M-x debug-on-entry= 然后输入要调试的命令(例如call-process)即可. 40 | 41 | * Emacs崩溃了怎么办 42 | 43 | 如果Emacs经常崩溃,那肯定要么Emacs版本太旧了,要么电脑太旧了. 44 | 45 | 假设你正在用Emacs写你的博士论文,你已经写了几个小时了却忘了保存文件了. 这时候Emacs突然崩溃,你要怎么才能恢复那些尚未保存的工作呢? 46 | 你只需要重新启动Emacs,然后运行 =M-x recover-session= 就会看到一个特殊的dired buffer. 这个buffer上有一行指示“Move to the session you want to recover, then type C-c C-c to select it.”. 然后你通过 =n= 或 =p= 移动到最近时间的那个item上再按下 =C-c C-c= 就行了. 47 | Emacs会访问对应的论文文件,然后从自动保存的数据中释放出那些尚未保存的修改,并将这些修改应用到访问论文文件的那个buffer中. 然后你就可以继续完成你的论文了. 但是在开始之前最好还是保存一下文件比较好. 48 | 49 | 谁会用Emacs而不是文字处理器来写博士论文呢? 有些org-mode和AUCTeX的使用者会这么做. [[http://www.youtube.com/watch?v=lsYdK0C2RvQ][这个视频]] 展示了如何用Emacs来生成一篇带代码片段的PDF文件. 这在你写论文时十分方便. 50 | 51 | ** 练习 52 | 53 | 用Emacs作一些修改,然后等待一段时间. 待Emacs自动保存后,在任务管理器中找到并杀掉Emacs进程,然后试试你能否恢复未保存的工作. 54 | -------------------------------------------------------------------------------- /emacs-common/Emacs数据库接口(EDBI).org: -------------------------------------------------------------------------------- 1 | #+TITLE: Emacs数据库接口(EDBI) 2 | #+URL: http://john.mercouris.online/emacs-database-interface.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2018年 06月 22日 星期五 14:56:16 CST] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | * 这是什么? 9 | :PROPERTIES: 10 | :CUSTOM_ID: what-is-it 11 | :END: 12 | 13 | Emacs数据库接口(EDBI)是一个连接任何数据库的有用工具,它带有一个抽象接口。就跟Emacs中的抽象的版本控制系统类似。 14 | ([[https://www.gnu.org/software/emacs/manual/html_node/emacs/Version-Control.html]]), 15 | 16 | EDBI可以帮助简化数据库一致性,并使多种数据库接口保持一致性。 17 | 18 | * 我为什么要在乎这个? 19 | :PROPERTIES: 20 | :CUSTOM_ID: why-should-i-care 21 | :END: 22 | 23 | EDBI有助于减轻管理和连接不同种类数据库的精神负担. 24 | 25 | * 如何安装 26 | :PROPERTIES: 27 | :CUSTOM_ID: how-do-i-install-it 28 | :END: 29 | 30 | 如果您在Emacs中安装Melpa。那么参照这里开始安装: [[http://melpa.milkbox.net/#/getting-started]]. 31 | 32 | 安装Melpa后,只需要简单地输入 =M-x package-install RET edbi RET= 就安装好EDBI了. 33 | 但要使用它还需要从CPAN上安装一些Perl包. 34 | CPAN 的意思是 "Comprehensive Perl Archive Network",它提供了一种安装Perl模块的方法。 35 | 36 | EDBI依赖于一个对数据库进行抽象的Perl接口来实现Emacs中的抽象。 37 | 更多关于抽象的内容可以看这里: [[https://metacpan.org/pod/DBI#connect]]. 38 | 其他还有如何在EDBI提示符中构建数据库的相关说明。 39 | 40 | 首先, 你需要安装如下模块: 41 | 42 | #+BEGIN_EXAMPLE 43 | RPC::EPC::Service 44 | DBI 45 | DBD::SQLite 46 | DBD::Pg 47 | DBD::mysql 48 | #+END_EXAMPLE 49 | 50 | 安装方法类似这样: =cpan install DBD::SQLite=. 它将安装对EDBI的SQLite支持。 51 | 52 | * 一个实际的例子:SQLITE 53 | :PROPERTIES: 54 | :CUSTOM_ID: a-practical-example-sqlite 55 | :END: 56 | 57 | 要使用EDBI,只需要输入 =M-x EDBI:open-db-viewer RET= 然后就会出现下面窗口 58 | 59 | [[http://john.mercouris.online/images/edbi-dialog-open.png]] 60 | 61 | 您可以在这里输入信息,但安装 EDBI 的SQLite专用报会让操作简单一些。 62 | 63 | 方法是,执行 =M-x package-install edbi-sqlite RET=. 64 | 之后你可以执行 =M-x edbi-sqlite RET= 并输入要打开的SQLite数据库 65 | 66 | [[http://john.mercouris.online/images/sqlite-open.png]] 67 | 68 | 打开数据库后,你会看到这样的屏幕: 69 | 70 | [[http://john.mercouris.online/images/edbi-database-view.png]] 71 | 72 | 屏幕中现实数据库中的所有表。输入 =?= 可以查看所有可用命令,显示结果如下: 73 | 74 | [[http://john.mercouris.online/images/edbi-help.png]] 75 | 76 | 选择某个选项,按下 =RET= 将出现下级操作项并显示表中的数据。如下所示 77 | 78 | [[http://john.mercouris.online/images/edbi-query.png]] 79 | 80 | 如你所见,这是一个查询。你可以编辑查询,并使用 =C-c c= 重新提交。 81 | 如此你就可以以更灵活的方式检查,修改和遍历数据了。 82 | 83 | 感谢阅读,希望你喜欢! 84 | -------------------------------------------------------------------------------- /emacs-common/Emacs键盘宏中的计数器.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Emacs键盘宏中的计数器 2 | #+URL: http://pragmaticemacs.com/emacs/macro-counters/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-07-09 六 20:40] 6 | #+OPTIONS: ^:{} 7 | #+TITLE: macro-counters 8 | 9 | 10 | 我[[http://pragmaticemacs.com/emacs/naming-and-saving-macros-for-repetitive-tasks/][曾经写过一篇]] 文章关于如何录制并回放键盘宏完成重复任务的文章. 键盘宏用有一个 [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Keyboard-Macro-Counter.html][计数器]] 可以允许你插入一个数值,并且在每次重放宏时自动进行累加. 11 | 12 | 例如,在一个新行处,按下 =C-x (= 开始录制宏,然后按下 =C-a= 移动到行首, 按下 =C-x C-k C-i= 插入宏计数器的值(默认为0),再按下 =RET= 新起一行, 再按下 =C=x )= 结束宏录制. 13 | 在用 =C-x e= 运行过一次宏后,可以直接按下 =e= 来重放该宏. 最后你会得到类似下面的结果: 14 | 15 | #+BEGIN_EXAMPLE 16 | 0 17 | 1 18 | 2 19 | 3 20 | 4 21 | #+END_EXAMPLE 22 | 23 | 每次你定义一个新宏,计数器都是从0开始. 要设置初值为其他值,需要再定义或调用宏之前按下 =C-x C-k C-c= 重新设置心智. 24 | -------------------------------------------------------------------------------- /emacs-common/INSIDE_EMACS变量.org: -------------------------------------------------------------------------------- 1 | #+TITLE: INSIDE_EMACS 变量 2 | #+URL: http://mbork.pl/2018-07-29_2018-07-29_The_INSIDE_EMACS_variable 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2018年 08月 19日 星期日 20:51:56 CST] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 在 [[http://mbork.pl/2018-06-10_Git_diff_in_Eshell][尝试]] [[http://mbork.pl/2018-07-16_Eshell_aliases][使用]]Eshell和Emacs中的shell时, 我发现了一条有意思的信息. 9 | 它对我本人意义不大, 因为我很少使用 =M-x shell= 和 =M-x term=, 相比之下我更喜欢使用Eshell, 但有些人可能会有所帮助. 10 | 当你在Emacs的非elisp shell中运行脚本/命令时, 环境变量 =INSIDE_EMACS= 会被设置为非空,这样脚本就能识别出它是运行在EMACS中的了. 11 | -------------------------------------------------------------------------------- /emacs-common/Spacemacs中的项目.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Spacemacs中的项目 2 | #+URL: https://jaketrent.com/post/projects-in-spacemacs/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2020年 01月 14日 星期二 19:30:24 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | Spacemacs上的项目非常简单,也很轻量级,非常棒. 9 | 10 | * 没有新项目 11 | :PROPERTIES: 12 | :CUSTOM_ID: no-new-project 13 | :END: 14 | 15 | 在Spacemacs中,你永远不需要进入Spacemacs菜单然后选择“新项目”。我的意思是…你的键盘很棒,按下 =Cmd-N= 就能新一个项目。 16 | 但在Spacemacs中你甚至无需这样做。你只需在文件系统的任何地方开始编辑文件即可。 17 | 你无需专门新建一个新项目、选择技术背景、勾选一堆选项、生成脚手架代码、加载项目类型支持等。编辑就是了。 18 | 19 | * 没有项目文件 20 | :PROPERTIES: 21 | :CUSTOM_ID: no-project-files 22 | :END: 23 | 24 | 许多编辑器或IDE都有项目文件的概念。它们通常位于代码目录的某个隐藏目录内。像 =.vscode= =.idea= 之类的东西。 25 | 这些文件通常存储与开发环境相关的项目配置或类似的东西。Spacemacs不会为你的代码加上这类文件。 26 | 27 | * Git跟踪项目 28 | :PROPERTIES: 29 | :CUSTOM_ID: projects-tracked-by-git 30 | :END: 31 | 32 | 无需创建新项目/编辑器相关的文件真是太棒了。然而,Spacemacs上有这么一个类似项目的东西。它实际上就是Spacemacs为你保存的曾经编辑过的本地git存储库列表。 33 | 34 | 要将某个项目添加到该列表中,只需要编辑本地git repo(带有 =.git= 子目录)中的文件即可。 35 | 36 | * 项目命令 37 | :PROPERTIES: 38 | :CUSTOM_ID: project-commands 39 | :END: 40 | 41 | 按下 =SPC p p= 调用 =helm-project-switch-project= 命令就可以检索这个项目列表,你可以选择一个最近的项目,然后编辑其中一个文件。 42 | 43 | Once in a project, to open a file in that project, type =SPC p f= for =helm-project-find-file=. 44 | 在项目中按下 =SPC p f= 调用 =helm-project-find-file= 命令能打开该项目中的文件。 45 | 46 | 因此,项目的启动、识别和导航在Spacemacs中都非常简单和轻量级。 47 | Spacemacs中的[[https://github.com/bbatsov/projectile][projectile]]还有许多其他有用的项目命令。 48 | 49 | 你还喜欢Spacemacs的哪些其他功能呢? 50 | -------------------------------------------------------------------------------- /emacs-common/Windows下用Emacs编辑远程文件.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Windows下用Emacs编辑远程文件 2 | #+URL: http://rentes.github.io/emacs/windows/ssh/2016/08/25/Editing-Remote-Files-With-Emacs-Under-Windows/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-09-18 日 20:31] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | 最近我实现了用Emacs(它是我最爱的编辑器之一)在Windows 10上编辑远程文件. 实现方法很简单,只需要将下面几行内容添加到 emacs 的 =init.el= 配置中就行了: 10 | 11 | #+BEGIN_SRC emacs-lisp 12 | (require 'tramp) 13 | (set-default 'tramp-auto-save-directory "C:\\Users\\\\AppData\\Local\\Temp") 14 | (set-default 'tramp-default-method "plink") 15 | #+END_SRC 16 | 17 | 需要将上面的 ~~ 替换为你的Windows用户. 18 | 19 | 另外也别忘了检查一下环境变量 =%Path%= 是否包含了 =plink.exe= 所在目录. 20 | 21 | 添加好配置后,通过运行命令 =M-x load-file= 重新加载 =init.el= 配置文件. 22 | 23 | 然后按两下回车表示接受默认的文件名, 之后就可以用 =C-x C-f= 打开远程文件了. 24 | 25 | 只不过指定远程文件的连接字符串应该遵守格式 =/@:=. 26 | 27 | 按下回车后然后收入远程用户的密码就OK了. 28 | -------------------------------------------------------------------------------- /emacs-common/directory-local变量快速指南.org: -------------------------------------------------------------------------------- 1 | #+TITLE: directory-local变量快速指南 2 | #+URL: http://endlessparentheses.com/a-quick-guide-to-directory-local-variables.html?source=rss 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-07-07 四 19:49] 6 | #+OPTIONS: ^:{} 7 | 8 | 我经常被问到的一个关于CIDER的问题是"我如何基于每个项目配置不同的XXX?". 9 | 有时你会发现有人建议(甚至是去实现)将一些复杂的可配置变量根据不同情况设置不同的值, 幸运的是,我们无须怎么费力就能做到这一点. Emacs已经内置了directory-local(缩写为dir-local)变量来满足这一需求. 10 | 11 | 望文生义, dir-local变量的值对指定目录(及其子目录)下的所有文件都生效. 当然你也可以在major-mode或子目录中重新设置该变量的值. 要配置一个dir-local变量,步骤为: 12 | 13 | 1. 调用 =M-x [[http://doc.endlessparentheses.com/Fun/add-dir-local-variable][add-dir-local-variable=]]. 14 | 2. 该命令会提示你依次输入 dir-local变量要在哪个major-mode下生效,变量名称以及变量的值. 15 | 3. 最后,它会再当前目录下找到或新建一个名为 =.dir-locals.el= 的文件. 保存该文件,配置就完成了. 16 | 17 | 经过上面的步骤之后,你就配置了一个变量,该变量在当前目录下的任意位置的值都是指定的值. 因此,请确保你是在项目的根目录执行的这个命令. 若你使用 [[https://github.com/bbatsov/projectile][Projectile]], =projectile-edit-dir-locals= 这个命令就会做这些动作. 18 | 19 | 还有些事情需要注意一下: 20 | 21 | * 若你再输入major-mode时输入了nil,则表示对所有的major-mode都生效. 22 | * 在提示输入变量名是,可以按下TAB键进行补全. 23 | * 若你设置的变量名为eval,则随后应该输入的时一个lisp表达式,而不是一个值. 每次访问该目录下的文件时,都会执行一次该lisp表达式. 24 | * 设置完dir-local变量后,哪些预先已经打开的文件需要调用[[http://doc.endlessparentheses.com/Fun/revert-buffer][revert-buffer]] 才能让这些dir-local变量生效. 25 | -------------------------------------------------------------------------------- /emacs-common/一个非开发者是如何使用Emacs的.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 一个非开发者是如何使用Emacs的 2 | #+URL: http://standardsandfreedom.net/index.php/2016/10/09/an-emacs-update/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2016-11-26 六 20:21] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 我已经有一阵子没有写关于Emacs--尤其是我个人使用Emacs经验的博客了. 10 | 我刚开始接触Emacs是因为我当时在寻找一款能同时处理HTML和CSS格式的编辑器; 接着我发现Emacs居然还是一个很不错的IRC客户端,而且我甚至还能使用Org mode来做一些项目管理的工作. 说起来那大概是2013到2014年初的事情了. 11 | 我完全被Emacs的无所不能所震惊了. 于是我开始每天使用Org-mode;为了更好的地理解Emacs也为了学习一门编程语言,我开始学习elisp. 12 | 13 | 需要说明的是: 我并不是一名软件开发者. 除了帮朋友创建和维护一些网站外,我并不怎么码代码. 不过我依然想在这里分享一下我的Emacs日常使用经验. 14 | 15 | 我使用Emacs的理由简单而无法反驳,那就是:我们使用计算机做的最多的事情就是编辑与处理文本. 16 | 当你在进行软件开发或文本编辑的时候,这是很明显的. 17 | 但是当你处理电子邮件,在IRC上聊天,管理文件与文件夹,发Twitter,阅读RSS,浏览web时,其实你也是在编辑与处理文本. 18 | 19 | 简而言之,很多计算机上的工作都可以看成是文本编辑. 20 | 一旦你能够认可这个观点,你就能够用文本编辑器(例如Emacs)来完成许多的事情. 21 | 就我自己来说,我常用Emacs来完成以下任务: 22 | 23 | 电子邮件: 我很乐于-甚至可以说是自豪地宣布,我使用mu4e(以及一系列Emacs为email提供的工具)来处理大多数的电子邮件. 24 | 我现在几乎不怎么用 =Evolution= 了,它是我除了Android上的 =K9= 以外,唯一的一款图形化email客户端了. 25 | Mu4e能够满足我在处理电子邮件上的大部分需求. 事实上,除了在收发那些由Outlook 2014发出的诡异格式的email时需要我做出少许调整之外,Mu4e的表现堪称完美. 26 | 我的电子邮件在本机上以MailDir格式保存的同时在线上也保留了一份副本. 27 | 我使用offlineimap来获取这些邮件,并将它们的状态在本机与服务器之间进行同步;我还使用mu mail indexer来索引并搜索这些邮件; 整个过程并不存在任何的性能问题,甚至比图形化的电子邮件客户端要快很多,尤其在搜索邮件的时候. 28 | Emacs在处理html email时出人意料地完美,除了之前说过的Outlook发出的那种邮件,我在浏览这些html时完全没有问题. 29 | 30 | Org-Mode: 这个是个大杀器. 我用它来进行任务管理,制定计划,记录笔记以及组织想法,这些都是Org-mode所擅长的. 31 | 我有事甚至用它来写博客,不过这需要与一个支持org文件或markdown文件的博客引擎相配合时才能显示出它的为例. 32 | 33 | 写博客: 我一般使用org格式或markdown格式来写博客. 34 | 不过关键在于如何高效地将Emacs与博客引擎整合起来. 目前我用的博客引擎是WordPress,不过看起来它并不那么尽如人意,未来我也许会考虑把它换掉. 35 | 36 | IRC: 我平时也不怎么用IRC, 但是这么多年来我也试过很多个IRC客户端都失败了. 这些客户端大多复杂而难用. 37 | 而当我使用ERC时,我感到非常的顺畅,这让我更愿意加入到IRC的讨论中. 38 | 39 | 文档管理: 使用 =Dired=, 它提供了大量的工具来进行文件管理, 又有一个使用Emacs的理由了. 40 | 41 | 使用eshell访问终端: 我平时不怎么要用到命令行,不过我的操作系统用的是Arch,因此每隔个两三天我总要用terminal来让系统升级一番. 42 | 43 | 文件编辑: That is as close to coding as I get to this day. 我为朋友们维护着一些网站,因此有时需要处理一些CSS,PHP脚本和Javascript脚本. 44 | 当然Emacs也提供了诸如web-mode等各式各样的工具来处理这些文件. 45 | 46 | 帮助文档: Emacs本身自带了大量的使用指南与手册,而且它也提供了大量的文档来教你学习eLisp. 你可以在Emacs中很容易的读到这些文档. 47 | 48 | 由于各种原因,我用的比较少的东西: 49 | 50 | 浏览网页: Emacs在这方面进步颇大,但是让我们面对现实吧,除非我想在线浏览文档或那些基本都是文字的网页,否则我几乎不会去用这种文字浏览器的. 51 | 52 | 访问Twitter: 我挺希望能够通过Emacs编辑,浏览Twitter的. 但是这个 twitter mode用起来太反人类了,而且还很慢. 53 | 如果有任何在Emacs上访问Twitter的建议,请一定告诉我. 54 | 55 | 浏览RSS feeds: 我也很希望通过Emacs浏览RSS feeds, 但是也是苦于没有找到好的package. 56 | Emacs自带了一个简单的RSS feed浏览器,但是它功能太差了. 另一个工具,elfeed,到是功能强大的多,但是可能我我太心急了,配置的不好,我觉得它用起来也不顺手. 57 | 目前elfeed还是会自动加载并更新我的feed内容,也许什么时候我会再考虑用它. 58 | 59 | 我希望能够鼓励到一些对Emacs有兴趣的人. 我并不是一名程序员,我所擅长的是文学,哲学以及历史. 60 | 但是从我的经验来看,Emacs真的很好用. 任何愿意花时间学习新事物的人都应该试着用一下它. 61 | -------------------------------------------------------------------------------- /emacs-common/为compilation-buffer增加交互功能.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 为compilation-buffer增加输入功能 2 | #+AUTHOR: lujun9972 3 | #+CATEGORY: emacs-common 4 | #+DATE: [2016-06-15 周三 18:00] 5 | #+OPTIONS: ^:{} 6 | 7 | Emacs的 =compile= 命令是一个未被充分利用的工具. 它允许你运行任意的构建工具并且为多种编程语言提供错误高亮与快速掉转到错误的功能. 但是许多的Emacs使用者依然习惯于切换到终端下运行make,lein test以及bundle exec. 然而 =compile= 命令有一个限制,那就是compilation buffer并不是真正的shell,因此若正在运行的命令需要用户输入额外信息(即使是简单的y/n确认),也没有办法输入这些信息. 8 | 9 | 幸运的是,为其增加输入功能并不复杂. 下面的代码片段定义了两个命令. 第一个命令会提示你输入要输入的信息,然后将你的输入附上回车符,一起传递到低层的终端上, 该命令用于提示输入时或在REPL循环中. 第二个命令只是简单的传递我们按下得键, 主要用于回答y/n问题,或者使用 =C-d= 或 =C-j= 快速退出REPL. 10 | #+BEGIN_SRC emacs-lisp 11 | (defun endless/send-input (input &optional nl) 12 | "Send INPUT to the current process. 13 | Interactively also sends a terminating newline." 14 | (interactive "MInput: \nd") 15 | (let ((string (concat input (if nl "\n")))) 16 | ;; This is just for visual feedback. 17 | (let ((inhibit-read-only t)) 18 | (insert-before-markers string)) 19 | ;; This is the important part. 20 | (process-send-string 21 | (get-buffer-process (current-buffer)) 22 | string))) 23 | 24 | (defun endless/send-self () 25 | "Send the pressed key to the current process." 26 | (interactive) 27 | (endless/send-input 28 | (apply #'string 29 | (append (this-command-keys-vector) nil)))) 30 | 31 | (define-key compilation-mode-map (kbd "C-c i") 32 | #'endless/send-input) 33 | 34 | (dolist (key '("\C-d" "\C-j" "y" "n")) 35 | (define-key compilation-mode-map key 36 | #'endless/send-self)) 37 | #+END_SRC 38 | 39 | 几年来我一直忽略这个缺陷(This is something I’ve run into for years), 但是最终,我还是决定修正它,因为它使得当我的代码中包含binding.pry(它会产生一个REPL)时,无法在compilation buffer中运行Ruby的rspec命令. 现在我可以通过先按下 =C-c i= 来与REPL交互,也可以使用 =C-d= 快速退出这个REPL. 若你也遇到与我一样的情况,你还需要在你的 =.pryrc= 文件中设置下面选项.pryrc file. 40 | #+BEGIN_SRC ruby 41 | Pry.config.pager = false if ENV["INSIDE_EMACS"] 42 | #+END_SRC 43 | -------------------------------------------------------------------------------- /emacs-common/使用SVG在org-reveal幻灯片中现实动态图表.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 使用SVG在org-reveal幻灯片中现实动态图表 2 | #+URL: https://www-public.imtbs-tsp.eu/~berger_o/weblog/2019/11/26/displaying-animated-diagrams-in-org-reveal-slide-decks-with-svg/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2020年 01月 15日 星期三 19:22:57 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | [[https://github.com/yjwen/org-reveal/][org-reveal]]可以使用org-mode创建幻灯片。幻灯片以HTML文档的形式展现,使用[[https://revealjs.com/][reveal.js]]实现动画。 10 | 11 | reveal.js允许逐步地显示页面中的部分片段,比如项目符号列表之类。 12 | 13 | 它还可以将动态图展示为reveal.js片段,前提是这些图作为嵌入的SVG图像包含在HTML中。 14 | 15 | 在SVG元素中添加 ~class="fragment"~ (及其变体,包括 =data-fragment-index= 属性),可以使用与常规项目符号片段相同的JS动作来展示图表的动画部分。 16 | 17 | 技巧是在org-mode源中使用以下代码来将SVG图标嵌入到HTML源代码中: 18 | 19 | #+BEGIN_EXAMPLE 20 | #+BEGIN_EXPORT html 21 | 22 | ... 23 | 24 | #+END_EXPORT 25 | #+END_EXAMPLE 26 | 27 | 我是从[[https://inkscape.org/~doctormo/%E2%98%85group-photo-web-css-popover][这个演示]] 中获得灵感的,它展示了动态现实SVG内容的方法。 28 | 29 | 到目前为止,我已经开始用inkscape编辑图标,最后将整个标记从保存的inkscape文件复制到org文件的 =#+BEGIN_EXPORT= 嵌入式HTML中。 30 | 31 | 您可以在[[https://olberger.gitlab.io/org-teaching/slides]] 文档中看到这样一个动画的演示。(幻灯片26页:“SVG片段显示”)。 32 | 33 | 希望这个有帮助。 34 | -------------------------------------------------------------------------------- /emacs-common/使用deft快速创建笔记.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 使用deft快速创建笔记 2 | #+URL: http://pragmaticemacs.com/emacs/make-quick-notes-with-deft/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2020年 02月 10日 星期一 10:01:58 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 我倾向于根据主题和项目来组织笔记,所以我发现org-mode 的 org-capture 很适合于往某个特定文件中快速添加笔,比如添加一个TODO项或一个博文的想法(我会在适当的时候张贴 *很多* 关于org-mode 的文章,另外这里有一个关于org的概述,参见[[http://www.star.bris.ac.uk/bjm/emacs.html][我的教程页面]])。 10 | 然而,如果我想做一个不属于某个类别的更一般的笔记(比如会议记录)是,那么[[http://jblevins.org/projects/deft/][deft]]是一个不错的选择。 11 | 12 | 这篇文章是对deft的简短介绍,我将在后续文章中描述我的一些调整。 13 | 14 | [[http://jblevins.org/projects/deft/][Deft]]是一个emacs包(可以通过 =package-list-packages= 获得),它允许您快速创建、搜索和添加笔记文件。它的灵感来自著名的Mac程序Velcity。 15 | 16 | 我的使用方法是启动deft,输入一个搜索字符串来快速找到我想要添加笔记的文件,或者动态创建一个新文件,然后添加我的笔记,退出deft,回到我之前的位置。 17 | 18 | Deft的工作方式很简单,将所有的笔记文件放在一个目录中,这些文件本身是简单的文本文件(如果你喜欢,也可以是org-mode文件),你可以在任何地方查看和编辑这些文件,也可以通过Deft查看和编辑。 19 | Deft提供了一个很好的快速界面用于查找/创建并打开正确的文件进行编辑。 20 | 21 | 使用 =M-x deft= 启动deft后,你将看到deft目录中的文件列表,其中包含简短的摘要信息。 22 | 然后键入一个搜索字符串,列表将动态筛选到文件名或内容匹配到字符串的文件。使用箭头键在文件列表中上下移动,然后按下回车打开该文件进行编辑。 23 | 24 | 如果没有与搜索字符串匹配的文件,那么按回车键将创建一个新文件,其名称为搜索的字符串。 25 | 如果希望创建某个预设名称的新文件,请输入 =C-c C-n=. 26 | 27 | 还可以从deft缓冲区重命名文件 =C-c C-r= 和删除文件 =C-c C-d=. 28 | 29 | 使用 =C-c C-q= 退出deft。 30 | 31 | 下面几个例子做了不错的展示。在第一个示例中,我启动deft并键入搜索字符串来查找现有文件,然后打开该文件进行编辑。本例中,我寻找的是有关2014年开放日的文件。 32 | 33 | [[http://pragmaticemacs.com/wp-content/uploads/2015/05/wpid-deft1.gif]] 34 | 35 | 在第二个示例中,我启动deft并输入一个不匹配任何文件的搜索字符串,然后根据搜索字符串创建一个具有名称的新文件。这里我搜索的字符串是“open day 2015”,它不匹配任何东西,所以deft为我创建了一个新文件。 36 | 37 | [[http://pragmaticemacs.com/wp-content/uploads/2015/05/wpid-deft2.gif]] 38 | 39 | 安装了deft之后,将以下内容添加到emacs配置文件中,就能获得面的能力了 40 | 41 | #+begin_src emacs-lisp 42 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 43 | ;; deft ;; 44 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 45 | (require 'deft) 46 | (setq deft-directory "~/docs/deft") 47 | (setq deft-extensions '("org")) 48 | (setq deft-default-extension "org") 49 | (setq deft-text-mode 'org-mode) 50 | (setq deft-use-filename-as-title t) 51 | (setq deft-use-filter-string-for-filename t) 52 | (setq deft-auto-save-interval 0) 53 | ;;key to launch deft 54 | (global-set-key (kbd "C-c d") 'deft) 55 | #+end_src 56 | 57 | 在这个配置中,我的deft文件都存储在 =~/docs/deft/= 中,并以 =.org= 作为文件扩展名,deft会在org模式下打开它们进行编辑。 58 | 59 | 选项 =(setq deft-use-filename-as-title t)= 告诉deft若字符串与现有文件不匹配,则使用搜索字符串作为文件名生成新文件。 60 | 61 | 这些就是deft的基础内容,但请继续关注后续的调整,这些调整会使deft的体验更加顺畅(至少对我来说)。 62 | 63 | * 更新 64 | :PROPERTIES: 65 | :CUSTOM_ID: orgheadline1 66 | :END: 67 | 68 | deft更新后引入了一种新的方法来告诉deft使用过滤内容来创建文件名 69 | 70 | #+begin_src emacs-lisp 71 | (setq deft-use-filter-string-for-filename t) 72 | #+end_src 73 | 74 | 我已经将它添加到上面的设置代码中。 75 | -------------------------------------------------------------------------------- /emacs-common/使用ido进行Emacs补全.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 使用ido进行Emacs补全 2 | #+URL: https://benaiah.me/posts/using-ido-emacs-completion/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2020年 02月 10日 星期一 10:31:25 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | * 使用ido进行Emacs补全 9 | :PROPERTIES: 10 | :CUSTOM_ID: using-ido-for-emacs-completion 11 | :END: 12 | 13 | 我是[[https://benaiah.me/posts/using-ido-emacs-completion/ido][ido]]的重度用户——我用它处理我所有的补全,无论是 =execut-extencommand=, =find-file=,还是其他任何东西。然而,ido的默认配置还有很多不足之处: 14 | 15 | - 它默认显示结果的方式非常难以阅读,因为所有补全项都在一个行中列出,因此你不能用眼睛快速跳到下一个项目。 16 | - 它默认只根据前缀进行补全 17 | - 使用ido来补全所有东西有点费劲 18 | 19 | 幸运的是,通过几个包和一点配置我们就能修正这些问题: 20 | 21 | - =ido-everywhere=,当设置为 =t= 时,会将ido用于更多地方的补全。 22 | - =ido-virtual-buffers= 本身不做任何修改,但它确实将recentf文件添加到 =find-file= 的补全项中,这非常方便。 23 | - [[https://github.com/creichert/ido-vertical-mode.el][ido-vertical-mode]] 使ido每个结果显示一行,每次显示的行数是可配置的(尽管我只使用默认值)。 24 | - [[https://github.com/lewang/flx/blob/master/flx-ido.el][flx-ido]]通过使用[[https://github.com/lewang/flx][flx]]补全引擎修复不足。 25 | - [[https://github.com/scottjad/ido-hacks][ido-hacks]] 和 [[https://github.com/DarwinAwardWinner/ido-ubiquitous][ido-ubiquitous]] 一起使ido补全了我想要的任何内容 26 | 27 | 我的全部ido配置如下: 28 | 29 | #+begin_src emacs-lisp 30 | (use-package ido :quelpa :config 31 | (setq ido-auto-merge-delay-time 99999999) 32 | (setq ido-everywhere t) 33 | (setq ido-virtual-buffers t) 34 | (ido-mode)) 35 | (use-package flx-ido :quelpa :requires ido :config (flx-ido-mode)) 36 | (use-package ido-vertical-mode :quelpa :requires ido :config (ido-vertical-mode)) 37 | (use-package ido-hacks :quelpa (:stable nil) :requires ido :config (ido-hacks-mode)) 38 | (use-package ido-ubiquitous :quelpa :requires ido :config (ido-ubiquitous-mode)) 39 | #+end_src 40 | 41 | 祝你开心 42 | -------------------------------------------------------------------------------- /emacs-common/使用imenu定位到use-package配置块.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 使用imenu定位到use-package配置块 2 | #+URL: http://irreal.org/blog/?p=5916 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2017-02-13 周一 11:18] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 几年前, 我曾经分享过一个 [[http://irreal.org/blog/?p=3979][Jordon Biondo的gist代码片段,]] 它允许使用 =imenu= 来定位 =use-package= 的配置块. 10 | 这个功能很实用,我推荐大家如果需要花费大量时间修改Emacs配置的话,都可以用用该功能. 11 | 12 | 不过今天 [[http://irreal.org/blog/?p=3979#comment-3140572899][Fran Burstall]] 告诉我, =use-package= 已经内建了该功能了. 你只需要在加载 =use-package= 前将 =use-package-enable-imenu-support= 设置为非nil就行了. 13 | 实现的相关代码与Biondo的很类似,不过它同时支持 =use-package= 和 =require=. 14 | Burstall不是很确定这项功能是什么加入到 =use-package= 中的,而我从代码上看也看不出来是什么时候加入该功能. 15 | 不过至少在当前版本(2.3)中,是已经有这个功能的. 16 | -------------------------------------------------------------------------------- /emacs-common/使用书签快速跳转到文件或目录处.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 使用书签快速跳转到文件或目录处 2 | #+URL: http://pragmaticemacs.com/emacs/use-bookmarks-to-jump-to-files-or-directories/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-08-30 周二 16:23] 6 | #+OPTIONS: ^:{} 7 | 8 | 在Emacs中,你可以将文件,目录,以及其他许多东西添加到[[https://www.gnu.org/software/emacs/manual/html_node/emacs/Bookmarks.html][书签]] 中, 这样你就能实现快速跳转了(就好像浏览器中的书签一样) 9 | 10 | 书签的基础用法很简单. 用 =C-x r m= 为当前文件/目录创建一个书签. 你可以为书签输入一个名字. 例如,我就将所有指向目录的书签以 =dir-= 开头来命名,这样的好处是它们就会在书签列表中显示在一起了. 11 | 12 | 按下 =C-x r b= 可以跳转到某个书签的位置,你会被要求输入要跳转的书签的名字. 如果像列出所有的书签,则按下 =C-x r l=. 13 | 14 | 有一个名为 [[https://www.emacswiki.org/emacs/BookmarkPlus][bookmarks+]] 的package为原生的书签添加了额外的功能. 但是我基本用不到这些功能. 15 | -------------------------------------------------------------------------------- /emacs-common/创建链接到info-buffer的链接.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 创建链接到info-buffer的链接 2 | #+URL: http://mbork.pl/2016-12-18_Generating_links_to_info_buffers 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2016-12-24 Sat 21:54] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 10 | 当编写与Emacs相关的电子邮件时(例如,回复help-gnu-emacs mailing list的时候),我经常要插入一个Info node的链接. 11 | 在Org-mode中插入Info node链接挺简单的,你可以使用 =org-store-link= 和 =org-insert-link= 来完成,然而默认情况下,这种Info node链接并不能被很好的被导出成HTML格式(他不会创建一个连接到online Emacs manual的链接). 12 | 不过,这里有个小技巧,(你甚至无需用到Org-mode): 你可以在 info buffer 中按下 =c= 键来拷贝当前node的 “链接”. 13 | 然后你可以在email中写下这么一段内容: 14 | 15 | #+BEGIN_SRC emacs-lisp 16 | (info "") 17 | #+END_SRC 18 | 19 | 然后你就可以让收信人将光标移动到这行语句的后面按下 =C-x C-e= 就能跳转到相应的Info node了. (当然,如果他/她并不使用Emacs来阅读电子邮件,那么他/她需要先将这句话拷贝到Emacs中) 20 | -------------------------------------------------------------------------------- /emacs-common/十倍提升Emacs性能.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 十倍提升Emacs性能 2 | #+URl: http://blog.binchen.org/posts/emacs-speed-up-1000.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-09-28 周三 17:31] 6 | #+OPTIONS: ^:{} 7 | 8 | 我对Emacs应用了如下的提速方法,然而还是对Emacs的性能不太满意: 9 | 10 | + 自动加载 packages 11 | + 延迟加载 packages 12 | + 编译 *.el 为 *.elc 13 | 14 | 经过一番探索, 我发现能够在1分钟内让我的Emacs提速10倍. 15 | 16 | 请注意,我说的可是运行性能,而不是启动速度哦. 17 | 18 | 解决方案其实很简单. 19 | 20 | 由于我用的是Linux,而且我的电脑也有足够多的内存(24G). 我完全可以将我的配置放到[[http://en.wikipedia.org/wiki/Tmpfs][内存中去]]. 21 | 22 | 1. 第一步, 将下行插入到 =/etc/fstab= 中,然后重启电脑: 23 | 24 | #+BEGIN_EXAMPLE 25 | tmpfs /tmp tmpfs nodev,nosuid,size=8G 0 0 26 | #+END_EXAMPLE 27 | 28 | 2. 第二步,运行下面这段脚本(emacs2ram): 29 | 30 | #+BEGIN_SRC sh :tangle "emacs2ram" 31 | #!/bin/sh 32 | 33 | if [ -z "$1" ];then 34 | echo "Usage:" 35 | echo " emacs2ram start" 36 | echo " emacs2ram restore" 37 | exit 1 38 | fi 39 | 40 | if [ "$1" == "start" ];then 41 | backup=emacs.d-backup 42 | link=.emacs.d 43 | volatile=/tmp/.emacs.d-$USER 44 | 45 | IFS= 46 | set -efu 47 | 48 | cd ~/ 49 | 50 | if [ ! -r $volatile ]; then 51 | mkdir -m0700 $volatile 52 | fi 53 | 54 | # link -> volatie does not exist 55 | if [ "$(readlink $link)" != "$volatile" ]; then 56 | # backup project at first 57 | mv $link $backup 58 | # create the link 59 | ln -s $volatile $link 60 | fi 61 | 62 | if [ -e $link/.unpacked ]; then 63 | echo "Sync .emacs.d from memory to backup ..." 64 | rsync -avq --delete --exclude .unpacked ./$link/ ./$backup/ 65 | echo "DONE!" 66 | else 67 | echo "Sync .emacs.d from disk to memory ..." 68 | rsync -avq ./$backup/ ./$link/ 69 | touch $link/.unpacked 70 | echo "DONE!" 71 | fi 72 | else 73 | echo "Moving .emacs.d back to disk ..." 74 | backup=$2-backup 75 | link=$2 76 | volatile=/tmp/$2-$USER 77 | cd ~/projs 78 | rm $link && mv $backup $link && rm -rf $volatile 79 | echo "DONE!" 80 | fi 81 | #+END_SRC 82 | 83 | 没了! 剩下的就像往常一样用Emacs就行了. 84 | 85 | 这段脚本最初是在列在ArchLinux Wiki上的. 我8年前就学会了这一招. 没想到我居然花了8年时间才真正用到了这一招. 86 | 87 | 另外, 我还用类似的脚本把所有的项目也放到内存中了. 88 | 89 | UPDATE: 我把我的项目管理脚本在[[https://gist.github.com/redguardtoo/596b1a9fd3eac1cedd13#file-proj2ram][gist]] 上也公开了. 它跟emacs2ram几乎一样的. 90 | -------------------------------------------------------------------------------- /emacs-common/合理地在Emacs中使用分页符.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 合理地在Emacs中使用分页符 2 | #+URL: https://ericjmritz.name/2015/08/29/using-page-breaks-in-gnu-emacs/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-10-12 周三 14:48] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | 今天我想说一说 [[https://www.gnu.org/software/emacs/][GNU Emacs]]中[[https://www.gnu.org/software/emacs/manual/html_node/emacs/Pages.html#Pages][页]] 的概念, 我是怎么使用页的,以及为什么我要使用页. 10 | 在我的经验看来页的作用经常被Emacs用户所忽视, 原因可能是都觉得它太丑了吧, 不过我会告诉你一个package可以美化它. 同时,我还会推荐你一个package可以实现很方便的在各页之间跳转. 11 | 12 | * A Crash Course Introduction to Pages 13 | 14 | Emacs中的‘页’指的是那些被form-feed字符(即^L,或者说Ctrl+L字符)所分割的那些文本. 在Emacs中可以按下 ~C-q C-l~ 来输入. 其中 ~C-q~ 是告诉Emacs直接插入你接下来输入的键. 15 | 正常情况下按下 ~C-l~ 会运行命令 ~recenter-top-bottom~. 因此我们不能通过直接按下 ~C-l~ 的方式插入form-feed字符. 16 | 不过 ~C-q C-l~ 则是告诉Emacs忽略 ~C-l~ 所绑定的命令,直接插入字面量^L, 而这个字符正是我门用来将文本分割成页的字符. 17 | 18 | 我们可以通过 ~C-x [~ 和 ~C-x ]~ 来向上/向下翻页. 他们对应的命令分别是 ~backward-page~ 和 ~forward-page~. 19 | 另外,按下 ~C-x C-p~ 可以选中我们当前页的所有内容. 20 | 21 | 关于页的介绍就差不多了. 下面来说说帮助我们在页之间跳转的那些package. 22 | 23 | * Why Use Pages? 24 | 25 | 我喜欢将内容按页分成几个章节,尤其在编程时更是如此. 26 | 比如我的工作室最近刚编写了一个名为 [[http://ejmr.github.io/chance.lua/][Chance]] 的库. [[https://github.com/ejmr/chance.lua/blob/master/chance.lua][其源代码]] 就用分页符分成了几个部分,当然,这你在Github上是看不出来的;这些分页符看起来就像是一个空行而已,不行你可以看看第228行. 27 | 不过在Emacs上,分页符可以看起来像是这样的. 28 | [[https://ericjmritz.files.wordpress.com/2015/08/emacs-pages.png]] 29 | 30 | 借助Steve Purcell那超赞的 [[https://github.com/purcell/page-break-lines][page-break-lines]] package可以让丑陋的^L字符显示成这些漂亮的横线. 31 | 32 | 库 ~Chance~ 使用了 [[http://stevedonovan.github.io/ldoc/][LDoc]] 来生成文档—LDoc类似于Javadoc的Lua版本. 在LDoc中,我们可以使用 =@section= 标注来将代码组织成相关的几块. 33 | 我在每个创建新 =@section= 的注释前都插入一个分页符,这样一来,源代码的各个块就与Emacs的各个页一一对应起来了. 34 | 35 | 如此,我就可以通过页在源代码的各个块之间跳转了. 我当然可以用前面提到过的 ~C-x [~ / ~C-x ]~ 来跳转,但其实还有更好的方案…. 36 | 37 | * A Nicer Way to Navigate Pages 38 | 39 | 我觉得用 [[https://github.com/david-christiansen/helm-pages][helm-pages]] 在页之间跳转挺不错的. 当我在 ~Chance~ 的源代码中调用 ~helm-pages~ 后,看到的结果是这样的: 40 | [[https://ericjmritz.files.wordpress.com/2015/08/emacs-helm-pages.png]] 41 | 它会列出每页的第一行内容,这样可以很容易的在代码的各部分之间进行跳转. 42 | ~Chance~ [[https://github.com/ejmr/chance.lua/blob/master/chance.spec.lua][的测试案例]] 也一样用分页符划分成了几部分, 用 ~helm-pages~ 来浏览的效果也不错. 而且每个page的第一行恰好是一个调用 ~describe()~ 的语句,这条语句说明了这部分代码测试的内容(我用[[http://olivinelabs.com/busted/][Busted]] 来进行单元测试). 43 | 44 | * Conclusion 45 | 46 | 是的,这是一篇很多的文章,不过也确实没什么好说的了. 希望本文能告诉你如何将代码划分出多个页,同时如何在这些页之间进行快速的跳转. 47 | 如果你从未用过页,那么请尝试一下,也许你会像我一样,发现这真是组织文件内容的好工具. 48 | 49 | Update (2015年12月15日): 强烈推荐阅读此文[[http://endlessparentheses.com/improving-page-navigation.html][info by Artur Balabarba on page-breaks]]. 50 | -------------------------------------------------------------------------------- /emacs-common/在Calendar中高亮有安排的日期.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Calendar中高亮有安排的日期 2 | #+URL: http://acidwords.com/posts/2017-02-16-displaying-org-mode-appointments-in-calendar.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2017-03-01 Wed 21:24] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | [[http://orgmode.org/manual/Agenda-Views.html][org-mode agenda]] 有一个功能,能够在Emacs Calendar上显示哪些日期有约会. 不过可惜,该功能默认并没有被开启. 10 | 我也是在浏览Emacs主题截图时偶然发现的. 11 | 12 | 在下面的屏幕截图中,你可以看到Calendar中有安排的日子都被高亮起来了. 13 | 14 | [[https://c1.staticflickr.com/3/2082/32109271913_31d143b549.jpg]] 15 | 16 | 配置很简单. 17 | 第一步, 设置 =calendar-mark-diary-entries-flag=: 18 | 19 | #+BEGIN_SRC emacs-lisp 20 | (setq calendar-mark-diary-entries-flag t) 21 | #+END_SRC 22 | 23 | 然后创建 =$HOME/diary= 文件, 并添加下面一行: 24 | 25 | #+BEGIN_EXAMPLE 26 | %%(org-diary) 27 | #+END_EXAMPLE 28 | 29 | 之后在Emacs中用org-agenda打开agenda view然后按 =c= 打开calendar;你会发现所有重要的日期都被高亮了. 30 | 当然,执行 31 | 32 | #+BEGIN_EXAMPLE 33 | M-x calendar 34 | #+END_EXAMPLE 35 | 36 | 效果一样,也能打开calendar. 37 | 38 | 你还能够反过来操作 - 要查看某个高亮的日期有哪些安排,只需要将光标移到特定的日期然后按下 =c=, Emacs就会打开 org-mode agenda view 并将光标放到合适的地方. 39 | 40 | * Using with appointments (appt) 41 | 42 | 如果你在使用org-mode的同时还使用 [[http://sachachua.com/blog/2007/11/setting-up-appointment-reminders-in-org/][appointments]] 的话, 那就要小心了,调用 =(appt-activate 1)= 会同时运行 =diary=. 43 | 这样的话,若在Emacs的配置文件中加了这一句话,那么在Emacs启动时就会弹出diary窗口. 44 | 我觉得这样很不好, 为了避开这种行为,需要设置 =appt-display-diary= 变量. 45 | 46 | 下面是我的 appointments 配置: 47 | 48 | #+BEGIN_SRC emacs-lisp 49 | (add-hook 'org-finalize-agenda-hook 50 | (lambda () 51 | (setq appt-message-warning-time 10 ;; warn 10 min in advance 52 | appt-display-diary nil ;; do not display diary when (appt-activate) is called 53 | appt-display-mode-line t ;; show in the modeline 54 | appt-display-format 'window ;; display notification in window 55 | calendar-mark-diary-entries-flag t) ;; mark diary entries in calendar 56 | (org-agenda-to-appt) ;; copy all agenda schedule to appointments 57 | (appt-activate 1))) ;; active appt (appointment notification) 58 | #+END_SRC 59 | -------------------------------------------------------------------------------- /emacs-common/在Emacs中操作 Github Gist.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Emacs中操作 Github Gist 2 | #+URL: http://www.admintome.com/blog/using-github-gist-emacs/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2019-03-15 五 22:10] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 本文, 我将会想你展示如何在EMACS中操作Github Gist. 10 | 这有助于你直接在Emacs上保存代码片段. 11 | 12 | * 安装 EMACS 上的 Github Gist 包 13 | 14 | 第一步就是为EMACS安装gist.el. 首先我们列出所有的包. 15 | 16 | #+BEGIN_EXAMPLE 17 | M-x package-list-packages RET 18 | #+END_EXAMPLE 19 | 20 | 在列表中找到 gist.el 包,即 ‘gist' 那一行. 21 | 22 | [[https://i0.wp.com/www.admintome.com/wp-content/uploads/2018/02/emacs-gist-install-1.png]] 23 | 24 | 在该行输入 *i* 和 *x* 来安装该包. 安装好包后, 回到EMACS主界面. 25 | 26 | 首先通过运行下面命令列出我们已有的 gists: 27 | 28 | #+BEGIN_EXAMPLE 29 | M-x gist-list RET 30 | #+END_EXAMPLE 31 | 32 | 第一次运行该命令会让你输入 github 用户名和密码. 输入这些信息后,就会向你展示当前Gist的列表 (如果你有的话). 下面显示的是我已有的Gist: 33 | 34 | [[https://i0.wp.com/www.admintome.com/wp-content/uploads/2018/02/gist-list.png]] 35 | 36 | 这跟我在网页上看的完全一致: 37 | 38 | [[https://i2.wp.com/www.admintome.com/wp-content/uploads/2018/02/my-gists.png]] 39 | 40 | 若你按下 *回车* 则 EMACS 会在一个新窗口中打开该gist. 在这个新窗口中你可以像往常一样编辑代码片段. 还可以像往常一样通过 *C-x s* 保存它. 41 | 42 | * 创建新gist 43 | 44 | 我们可以编辑已有的Gist, 现在让我们来在EMACS中创建新的Gist. 退出并运行下面命令来创建一个名叫 *unary-if.py* 的新文件 45 | 46 | #+begin_src shell 47 | $ emacs unary-if.py 48 | #+end_src 49 | 50 | 添加下面代码到文件中然后保存之. 51 | 52 | #+begin_src python 53 | ## Simple gist to show how to do a python unary if statement 54 | flag = "True" 55 | result = True if flag == "True" else False 56 | print(result) 57 | #+end_src 58 | 59 | 把所有代码标记为区域,方法是在文本开头的地方按下 *C-Space* (译者注,这只在英文环境下有效,中文环境下用 *Ctrl-@* 来代替)然后移动光标直到高亮所有代码. 60 | 运行下面命令来保存该Gist: 61 | 62 | #+BEGIN_EXAMPLE 63 | M-x gist-region RET 64 | #+END_EXAMPLE 65 | 66 | 或者用下面命令创建一个私有Gist: 67 | 68 | #+BEGIN_EXAMPLE 69 | M-x gist-region-private RET 70 | #+END_EXAMPLE 71 | 72 | 现在你可以在网页上看到新Gist了: 73 | 74 | [[https://i0.wp.com/www.admintome.com/wp-content/uploads/2018/02/new-gist.png]] 75 | 76 | 该功能真酷,应该对你会有所帮助. 一如既往的, 我希望你会喜欢这篇在EMACS上操作Github Gist的文章. 77 | 如果你还没有注册我的newsletter,请在下面留言. 78 | 79 | -------------------------------------------------------------------------------- /emacs-common/在Emacs中查看日出日落的时间.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Emacs中查看日出日落的时间 2 | #+AUTHOR: lujun9972 3 | #+TAGS: emacs-common 4 | #+DATE: [2016-11-24 四 20:51] 5 | #+LANGUAGE: zh-CN 6 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 7 | 8 | 随着白天的时间原来越短, 我很好奇我所在地区的日出日落的时间是什么时候. 9 | 过去我一般是用google搜索 “sunrise| sunset ”, 不过我还是希望能够找到某个能在本机上运行的软件能够自动查出这个时间点. 10 | 而且作为一名Emacs用户,很自然的我希望能够在Emacs中运行这个软件. 11 | 经过一番折腾,我发现这项功能其实已经内置于Emacs的diary插件中了. 12 | 13 | * The finished product 14 | 15 | [[http://www.jmdeldin.com/images/emacs_sunrise.png]] 16 | 太平洋西北区确实很不错,就是白天的时间太短了. 17 | 18 | * Steps 19 | 20 | 1. 如果你没有配置过Org-mode, 你需要在 =~/.emacs.d/init.el= 中添加以下内容: 21 | 22 | #+BEGIN_SRC emacs-lisp 23 | (setq org-directory "/tmp/org") 24 | (setq org-agenda-files (file-expand-wildcards org-directory)) 25 | #+END_SRC 26 | 27 | 2. 然后自定你所在地区的经纬度. 你可以通过[[http://maps.google.com][Google地图]] 或 [[http://www.latlong.net/][LatLong]] 来找出所在地区的经纬度: 28 | 29 | #+BEGIN_SRC emacs-lisp 30 | (setq calendar-latitude 47.9790) 31 | (setq calendar-longitude -122.2021) 32 | (setq calendar-location-name "Everett, WA") 33 | #+END_SRC 34 | 35 | 3. 修改第一步中指定的其中一个Org agenda文件, 例如 =/tmp/test.org=: 36 | 37 | #+BEGIN_SRC org 38 | ,* Calendar 39 | %%(diary-sunrise-sunset) 40 | #+END_SRC 41 | 42 | 4. 保存并重新加载你的初始化文件, 然后执行 =M-x org-agenda=, 在按下 =a= 就能看到效果了. 43 | 44 | Tip: 如果你想看整年的日出/日落时间,那么可以执行 =M-x org-agenda RET a v y= 45 | -------------------------------------------------------------------------------- /emacs-common/在Emacs中用elfeed查看YouTube订阅.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Emacs中用elfeed查看YouTube订阅 2 | #+URL: https://codingquark.com/emacs/2019/05/16/emacs-elfeed-youtube.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2019年 06月 14日 星期五 18:23:48 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | * 目标 9 | :PROPERTIES: 10 | :CUSTOM_ID: goal 11 | :END: 12 | 13 | 在 elfeed 中获取订阅频道的所有上传视频,并且如果需要的话可以进行下载。 14 | 15 | * 依赖 16 | :PROPERTIES: 17 | :CUSTOM_ID: dependencies 18 | :END: 19 | 20 | 1. Emacs 21 | 2. [[https://github.com/skeeto/elfeed][elfeed]] 22 | 3. [[https://youtube-dl.org/][youtube-dl]] 23 | 24 | * 准备 25 | :PROPERTIES: 26 | :CUSTOM_ID: preparation 27 | :END: 28 | 29 | 从 [[https://www.youtube.com/subscription_manager][subscrition manager]] 处下载订阅的 =xml= 文件. 这个链接位于页面靠近底部的位置,名叫 “Export subscriptions”. 30 | 31 | 添加URL到 =elfeed-feeds= 中: 32 | 33 | #+begin_src emacs-lisp 34 | (setq elfeed-feeds 35 | '("https://www.youtube.com/feeds/videos.xml?channel_id=UC0sytTpk1adR_cfjHhiJ08Q")) 36 | #+end_src 37 | 38 | 更新初始化文件 39 | 40 | #+begin_src emacs-lisp 41 | ;; Set executable path 42 | (setq youtube-dl-path "/usr/bin/youtube-dl") 43 | ;; Set video storage path 44 | (setq youtube-dl-output-dir "~/Videos/") 45 | 46 | (defun elfeed-download-video () 47 | "Download a video using youtube-dl." 48 | (interactive) 49 | (async-shell-command (format "%s -o \"%s%s\" -f bestvideo+bestaudio %s" 50 | youtube-dl-path 51 | youtube-dl-output-dir 52 | "%(title)s.%(ext)s" 53 | (elfeed-entry-link elfeed-show-entry)))) 54 | 55 | ;; Add `youtube` tag to all videos 56 | (add-hook 'elfeed-new-entry-hook 57 | (elfeed-make-tagger :feed-url "youtube\\.com" 58 | :add '(video youtube))) 59 | #+end_src 60 | #+BEGIN_EXAMPLE 61 | #+END_EXAMPLE 62 | 63 | * 使用 64 | :PROPERTIES: 65 | :CUSTOM_ID: use 66 | :END: 67 | 68 | 更新 feed 后, 你就能看到订阅的视频了. 在某个视频上按下 =回车=, 然后运行 =M-x elfeed-download-video =. 69 | 等待命令结束, 视频就会保存到本地了. 70 | 71 | * 展示 72 | :PROPERTIES: 73 | :CUSTOM_ID: screenshots 74 | :END: 75 | 76 | [[https://codingquark.com/images/elfeed_feeds_list_1.png]] 77 | 78 | [[https://codingquark.com/images/elfeed_video_download_1.png]] 79 | -------------------------------------------------------------------------------- /emacs-common/在Emacs中禁用鼠标操作.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Emacs中禁用鼠标操作 2 | #+URL: http://endlessparentheses.com/disable-mouse-only-inside-emacs.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-08-08 周一 10:06] 6 | #+OPTIONS: ^:{} 7 | 8 | 随着触摸板变得越来越大,我们在输入时就越来越可能不小心接触到触摸板. 大多数的系统都有防护措施让你在输入时暂时屏蔽触摸板的功能,但是这在我使用Emacs时还远远不够. 9 | 在使用Emacs的过程中,我的手是一直放在键盘上的(当然也会放在触摸板上),因此即使我长时间没有输入,也不希望让触摸板重新被激活. 10 | 11 | 通过一个热键就可以永久性的禁用触摸屏,但是最满足我自己使用方式的解决方案是仅仅在Emacs中禁用触摸板. 12 | 13 | #+BEGIN_SRC emacs-lisp 14 | (define-minor-mode disable-mouse-mode 15 | "A minor-mode that disables all mouse keybinds." 16 | :global t 17 | :lighter " 🐭" 18 | :keymap (make-sparse-keymap)) 19 | 20 | (dolist (type '(mouse down-mouse drag-mouse 21 | double-mouse triple-mouse)) 22 | (dolist (prefix '("" C- M- S- M-S- C-M- C-S- C-M-S-)) 23 | ;; Yes, I actually HAD to go up to 7 here. 24 | (dotimes (n 7) 25 | (let ((k (format "%s%s-%s" prefix type n))) 26 | (define-key disable-mouse-mode-map 27 | (vector (intern k)) #'ignore))))) 28 | #+END_SRC 29 | 30 | 我们所做的不过是定义一个minor-mode,在这个minor-mode中将所有涉及鼠标的按键都绑定为ignore. 它只是基于[[http://stackoverflow.com/questions/4906534/disable-mouse-clicks-in-emacs/4906698#4906698][this StackOverflow answer]]中的代码做了少许改进. 31 | 当然要重新启用触摸板也很容易: 32 | 33 | #+BEGIN_SRC emacs-lisp 34 | (disable-mouse-mode 1) 35 | #+END_SRC 36 | 37 | 但是这种方案有两个局限条件: 38 | 39 | + 它无法区分触摸板与真正的鼠标操作,两者都被禁用了. 40 | + 还是有可能其他的mode又定义了与鼠标相关的快捷键. 但是这个问题不大,因为这种快捷键很少见,因此不小心触发它们的概率也很小. 41 | them by accident. 42 | 43 | * Update 29 Jun 2016 44 | 45 | 也许仅仅是巧合,似乎Steve Purcell也实现了类似的东西. 而且他已经将这个minor-mode封装成了package放到[[https://github.com/purcell/disable-mouse][Melpa package]]中了. 46 | -------------------------------------------------------------------------------- /emacs-common/在Emacs中编译.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Emacs中编译 2 | #+AUTHOR: lujun9972 3 | #+CATEGORY: emacs-common 4 | #+DATE: [2016-06-15 周三 20:21] 5 | #+OPTIONS: ^:{} 6 | 7 | 许多Emacs新手常常因为想定位编译出错的位置而询问如何获取buffer中行号. 然而这并不是定位编译出错位置的正确方法. 8 | 9 | Emacs内置了一个compilation mode,该mode定义在compile.el中. It uses a very generic text-based approach. 10 | 11 | 这是一个为Emacs新手准备的指南. 有经验的读者可以之间跳到本文最下面查看如何为error解析器增加新的规则. 12 | 13 | 14 | * 使用方法 15 | 16 | + 执行 =M-x compile=. 17 | + Emacs会提示你输入编译要运行的命令. 输入命令然后按回车. 18 | + Emacs运行编译命令,解析编译结果,拆分frame然后弹出结果buffer. 19 | 20 | 若编译时出现error,有很多方法可以跳转到出现error的地方. 21 | 22 | + 若你按下 =C-x `=,就会跳转到当前buffer的下一个error处. *这样你根本无需查询出错的行号* 23 | 24 | + 若你切换到编译结果的buffer(一般名为*compilation*),你可以直接在报错信息那一行上直接按下回车,就能跳转到出错信息对应的位置了. 25 | 26 | 若已经运行过 =M-x compile= ,你可以通过 =M-x recompile= 来使用上一次编译时的命令来重新编译. 若你想使用不同的编译命令,则只需再次运行 =M-x compile= 即可. 27 | 28 | 当然,就像大多数Emacs中的prompt一样,你也可以通过 =M-n= 和 =M-p= 遍历你的输入历史. 29 | 30 | * 相关快捷键 31 | 32 | 当处于编译buffer中时,可以使用以下快捷键: 33 | 34 | + g :: 重新编译 35 | + q :: 退出编译buffer 36 | + TAB / M-n :: 跳转到下一个error 37 | + S-TAB (shift tab / backtab) / M-p :: 跳转到上一个error 38 | + C-c C-k :: 杀掉编译进程 39 | 40 | * 一般配置 41 | 42 | 若你习惯于一键编译, 你可以向下面这个例子一样,将 =recompile= 命令绑定到 =F9= 键上. 43 | #+BEGIN_SRC emacs-lisp 44 | ;; eval this or place it your .emacs 45 | (global-set-key (kbd "") 'recompile) 46 | #+END_SRC 47 | 48 | * 工作原理是什么? 49 | 50 | 变量 =compilation-error-regexp-alist= 是一个存储了如何解析出错信息规则(从错误信息中抽取出文件名,行号等内容)的列表. 51 | 52 | 列表中的每个元素可以是一个symbol或者列表. 53 | 54 | + 若元素是一个symbol,则Emacs会根据该symbol从变量 =compilation-error-regexp-alist-alist= 中寻找真正的解析规则. symbol的名字常常就是编译程序的名字. 55 | + 若元素是一个list,则该list被作为解析规则. 只有当定义的规则很普遍化或者缺失很懒的情况下才使用这种格式. 56 | 57 | 最简单的规则是以下格式的列表 58 | #+BEGIN_SRC emacs-lisp 59 | (REGEXP FILE [LINE COLUMN]) 60 | #+END_SRC 61 | + REGEXP 是一个匹配输出的正则表达式字符串. 62 | + 剩下的元素是正则表达式中对应内容的分组编号. 你至少需要提供文件名的分组编号,其他的信息是可选的. 63 | 64 | 当然,你还可以提供其他用于的信息. 65 | 66 | 想进一步了解,可以查看该变量的文档字符串(C-h v compilation-error-regexp-alist RET). 67 | 68 | * 添加新规则 69 | 70 | 若你编译的结果Emacs无法正确解析,则你需要添加自己的规则. 假设编译结果有如下一行错误信息. 71 | #+BEGIN_EXAMPLE 72 | Error in the file foobar.c at the line 42 73 | #+END_EXAMPLE 74 | 75 | 你可以使用下面的正则表达式从中抽取出文件名与行号: 76 | #+BEGIN_EXAMPLE 77 | "^Error in the file \\(.+\\) at the line \\([0-9]+\\)$" 78 | #+END_EXAMPLE 79 | 80 | 其中文件名被第一个分组捕获,行号被第二个分组捕获. 因此最终规则如下: 81 | #+BEGIN_SRC emacs-lisp 82 | ("^Error in the file \\(.+\\) at the line \\([0-9]+\\)$" 1 2) 83 | #+END_SRC 84 | 85 | 要应用该规则,你可以在*scratch* buffer中执行以下代码,或者使用 =M-:= 来执行以下代码 86 | #+BEGIN_SRC emacs-lisp 87 | (add-to-list 88 | 'compilation-error-regexp-alist 89 | '("^Error in the file \\(.+\\) at the line \\([0-9]+\\)$" 1 2)) 90 | #+END_SRC 91 | 92 | 将上面代码存入你的 =.emacs= 文件中,使之永久有效. 93 | 94 | 若你为某个工具添加了解析规则,而该工具对他人有用的话,请不要犹豫,将其分享到Emacs mailing-list中去吧! 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /emacs-common/在Windows上安装支持PDF与Xwidgets网络浏览功能的Emacs.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Windows上安装支持PDF与Xwidgets网络浏览功能的Emacs 2 | #+URL: https://tuhdo.github.io/setup-emacs-windows.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: raw 5 | #+DATE: [2016-08-31 三 12:57] 6 | #+OPTIONS: ^:{} 7 | 8 | 安装指南: 9 | 10 | * 前提条件 11 | 12 | + 从[[https://cygwin.com/install.html][Cygwin安装页面]] 下载cygwin安装程序. 13 | + 如果你只需要阅读PDF,那么只需要安装Cygwin上自带的Emacs就行了,如果你还需要浏览web,那么你需要从源代码上编译自己的Emacs25. 14 | 15 | * With PDF reader 16 | 17 | [[https://tuhdo.github.io/static/setup-emacs-windows/pdf_tools_windows_latex.gif][DEMO]] (Notice how I press some text in PDF file and Latex buffer jumps to corresponding automatically). 18 | 19 | 首先, 不要从MELPA安装package; 你需要从仓库中手工安装 =pdf-tools=. 然后,你需要安装 =Cygwin=. 同时还需要安装一下这些依赖: 20 | 21 | 如果你不打算使用Xwidget浏览器,那么只需要安装Cygwin中的Emacs就行了: 22 | 23 | + emacs-w32 (Editors category) 24 | 25 | 然后安装PDF工具的工具链: 26 | 27 | + make (Devel category) 28 | + gcc-core (Devel category) 29 | + gcc-g++ (Devel category) 30 | + autoconf (Devel category) 31 | + automake (Devel category) 32 | + perl (Perl category) 33 | 34 | 还需要安装一些开发库: 35 | 36 | + libpoppler-devel (Lib category) 37 | + libpoppler-glib-devel (Lib category) 38 | 39 | 安装方式是在搜索框中输入上面依赖库的名称,然后选择安装即可. 40 | 41 | 所有的依赖库都安装好后,克隆pdf-tools: 42 | 43 | #+BEGIN_SRC sh 44 | git clone https://github.com/politza/pdf-tools 45 | #+END_SRC 46 | 47 | 然后进入df-tools目录构建它: 48 | 49 | #+BEGIN_SRC sh 50 | make 51 | #+END_SRC 52 | 53 | 编译完成过后,将文件 =server/epdfinfo.exe= 拷贝到 =/usr/bin/= 中: 54 | 55 | #+BEGIN_SRC sh 56 | cp server/epdfinfo.exe /usr/bin/ 57 | #+END_SRC 58 | 59 | 还是在Cygwin中,运行emacs. 然后在Emacs中运行Emacs命令: 60 | 61 | + M-x package-install-file 62 | + Press RET 63 | + 选择 pdf-tools-${VERSION}.tar RET (该tar文件位于你克隆下来的仓库目录中). 64 | 65 | 最后运行 =pdf-tools-install= 就结了. 你也可以将下面的配置放到你的初始化文件中: 66 | 67 | #+BEGIN_SRC emacs-lisp 68 | (pdf-tools-install) 69 | (setq pdf-info-epdfinfo-program "/usr/bin/epdfinfo.exe") 70 | #+END_SRC 71 | 72 | * With Xwidgets browser 73 | 74 | [[https://tuhdo.github.io/static/setup-emacs-windows/emacs_xwidgets_windows.png][DEMO]] 75 | 76 | 构建支持Xwidgets的Emacs依赖于以下开发库: 77 | 78 | + libgif-devel (Graphics category) 79 | + libtiff-devel (Lib category) 80 | + libjpeg-devel (Devel category) 81 | + libxpm-devel (Devel category) 82 | + libgnutls-devel (Devel category) 83 | + libpng-devel (Devel category) 84 | + libncurses-devel (Lib category) 85 | + libgtk3-drivel (Lib category) 86 | + libwebkitgtk3.0-devel (Lib category) 87 | 88 | 然后安装 X11 server以显示Emacs: 89 | 90 | + xorg-server (X11 category) 91 | + xinit (X11 category) 92 | 93 | 再然后,打开Cygwin终端开始编译Emacs: 94 | 95 | #+BEGIN_SRC sh 96 | ./configure --with-xwidgets 97 | make 98 | make install 99 | #+END_SRC 100 | 101 | 最后,设置DISPLAY环境变量: 102 | 103 | #+BEGIN_SRC sh 104 | echo "export DISPLAY=:0.0" >> .bash_profile 105 | #+END_SRC 106 | 107 | 运行时, 先在开始菜单中启动X server: 108 | 109 | [[https://tuhdo.github.io/static/setup-emacs-windows/xserver_example.png][xserver_]] 110 | 111 | 然后在Cygwin终端中启动编译出来的Emacs25: 112 | 113 | #+BEGIN_SRC sh 114 | emacs 115 | #+END_SRC 116 | -------------------------------------------------------------------------------- /emacs-common/在Windows上配置CapsLock使之对Emacs友好.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Windows上配置CapsLock使之对Emacs友好 2 | #+URL: http://www.blogbyben.com/2016/08/an-emacs-friendly-caps-lock.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-09-07 周三 14:34] 6 | #+OPTIONS: ^:{} 7 | 8 | 也许这种方法大家早就知道了, 我发现在我的Windows10电脑上将 =Caps Lock= 按键修改成对Emacs友好的 =Hyper= 键后,Emacs好用多了. 9 | 具体来说,我是怎么做的: 10 | 11 | 第一步. 使用[[http://www.blogbyben.com/2011/03/tool-of-day-autohotkey.html][AutoHotKey]] 来[[https://autohotkey.com/docs/misc/Remap.htm][映射]] =Caps Lock= 键为 =Windows Menu= 键(AutoHotKey将之称为AppsKey). 12 | 13 | #+BEGIN_SRC ahk 14 | ;; Add this to your standard AutoHotKey configuration 15 | CapsLock::AppsKey 16 | #+END_SRC 17 | 18 | 第二步. 使用[[http://ergoemacs.org/emacs/emacs_hyper_super_keys.html][下面这段elisp代码]] 来捕获传递给emacs的Menu键,并将之映射成 =Hyper modifier=: 19 | 20 | #+BEGIN_SRC emacs-lisp 21 | ;; http://ergoemacs.org/emacs/emacs_hyper_super_keys.html 22 | (setq w32-pass-apps-to-system nil) 23 | (setq w32-apps-modifier 'hyper) ; Menu/App key 24 | #+END_SRC 25 | 26 | 第三步. 完成了! 我现在可以设置带 =H- modifier= 的快捷键了. 例如下面这段代码摘自我的[[http://www.blogbyben.com/2016/08/emacs-php-modern-and-far-more-complete.html][PHP配置]]: 27 | 28 | #+BEGIN_SRC emacs-lisp 29 | (defun bs-php-mode-hook () 30 | (local-set-key '[backtab] 'indent-relative) 31 | (local-set-key (kbd "") 'beginning-of-defun) 32 | (local-set-key (kbd "") 'end-of-defun) 33 | (auto-complete-mode t) 34 | (require 'ac-php) 35 | (setq ac-sources '(ac-source-php )) 36 | (yas-global-mode 1) 37 | (setq indent-tabs-mode nil) 38 | (setq php-template-compatibility nil) 39 | (setq c-basic-offset 2)) 40 | #+END_SRC 41 | 42 | 这段配置让我打开PHP文件后,再按下 =Caps Lock= 键,就可以通过左右按键在函数定义见跳转了. 43 | 44 | 这个键盘及快捷键的设定让我好像中了奖了一样,太爽了. 45 | -------------------------------------------------------------------------------- /emacs-common/在Windows平台上的Emacs中运行zsh shell.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Windows平台上的Emacs中运行zsh shell 2 | #+URL: https://www.onwebsecurity.com/configuration/zsh-shell-inside-emacs-on-windows.html 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2019-03-10 日 22:04] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | [[https://www.onwebsecurity.com/images/zsh-shell-inside-emacs-on-windows.png]] 10 | 11 | 运行跨平台shell(例如 Bash 或 zsh)的最大优势在于你能在多平台上使用同样的语法和脚本. 在Windows上设置 (替换) shells 挺麻烦的, 但所获得的回报远远超出这小小的付出. 12 | 13 | MSYS2 子系统允许你在Windows上运行 Bash 或 zsh 之类的shell. 使用 MSYS2 很重要的一点在于确保搜索路径都指向 MSYS2 子系统本身: 存在太多依赖关系了. 14 | 15 | MSYS2安装后默认的shell就是Bash; zsh 则可以通过包管理器进行安装: 16 | 17 | #+begin_src shell 18 | pacman -Sy zsh 19 | #+end_src 20 | 21 | 通过修改 =etc/passwd= 文件可以设置zsh作为默认shell, 例如: 22 | 23 | #+begin_src shell 24 | mkpasswd -c | sed -e 's/bash/zsh/' | tee -a /etc/passwd 25 | #+end_src 26 | 27 | 这会将默认shell从 bash 改成 zsh. 28 | 29 | 要在Windows上的Emacs运行 zsh 需要修改 =shell-file-name= 变量, 将它指向 MSYS2 子系统中的 zsh 二进制文件. 该二进制 shell 文件在 Emacs =exec-path= 变量中的某个地方. 30 | 31 | #+begin_src emacs-lisp 32 | (setq shell-file-name (executable-find "zsh.exe")) 33 | #+end_src 34 | 35 | 不要忘了修改 Emacs 的 PATH 环境变量, 因为 MSYS2 路径应该先于 Windows 路径. 接上一个例子, 假设 MSYS2 安装在 =c:\programs\msys2= 中,那么我们执行: 36 | 37 | #+begin_src emacs-lisp 38 | (setenv "PATH" "C:\\programs\\msys2\\mingw64\\bin;C:\\programs\\msys2\\usr\\local\\bin;C:\\programs\\msys2\\usr\\bin;C:\\Windows\\System32;C:\\Windows") 39 | #+end_src 40 | 41 | 在Emacs配置文件中设置好这两个变量后, 在Emacs中运行 =M-x shell= 应该就能看到熟悉的zsh提示符了. 42 | 43 | Emacs' 的终端设置 (eterm) 与 MSYS2 的标准终端设置(xterm-256color)不一样. 这意味着某些插件和主题(标识符)可能不能正常工作 - 尤其在使用 oh-my-zsh 时. 44 | 45 | 检测 zsh 否则在Emacs中运行很简单, 使用变量 =$INSIDE_EMACS= . 这段取自 =.zshrc= (当以交互式shell模式启动时会被加载)的代码片段会在zsh在Emacs中运行时启动 git 插件并更改主题 46 | 47 | #+begin_src shell 48 | # Disable some plugins while running in Emacs 49 | if [[ -n "$INSIDE_EMACS" ]]; then 50 | plugins=(git) 51 | ZSH_THEME="simple" 52 | else 53 | ZSH_THEME="compact-grey" 54 | fi 55 | #+end_src 56 | 57 | 通过在本地 =~/.ssh/config= 文件中将 =INSIDE_EMACS= 变量设置为 =SendEnv= 变量... 58 | 59 | #+begin_src conf 60 | Host myhost 61 | SendEnv INSIDE_EMACS 62 | #+end_src 63 | 64 | ... 同时在ssh服务器的 =/etc/ssh/sshd_config= 中设置为 =AcceptEnv= 变量 ... 65 | 66 | #+begin_src conf 67 | AcceptEnv LANG LC_* INSIDE_EMACS 68 | #+end_src 69 | 70 | 71 | ... 这使得在Emacs shell 会话中通过ssh登录另一个运行着zsh的ssh服务器也能工作的很好. 当在Windows下的Emacs中的zsh上通过ssh远程登录时,记得使用参数 =-t=. =-t= 参数会强制分配伪终端(之所以需要这样,时因为Windows下的Emacs并没有真正的tty). 72 | 73 | 跨平台, 开源真是个好东西... 74 | -------------------------------------------------------------------------------- /emacs-common/在Windows平台为Emacs24启用GnuTLS支持.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Windows平台为Emacs24启用GnuTLS支持 2 | #+URL: http://חנוך.se/diary/how_to_enable_GnuTLS_for_Emacs_24_on_Windows/index.en.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-08-31 周三 12:44] 6 | #+OPTIONS: ^:{} 7 | 8 | Emacs 24开始允许直接使用GnuTLS库来创建TLS网络链接,而无需再调用命令行工具了. 这对Windows平台的Emacs用户是个重大利好消息,因为Windows平台上缺少这种命令行工具. 9 | 10 | 不过要在Windows上启用原生GnuTLS支持还需要经过以下几个步骤: 11 | 12 | * Get a GnuTLS-enabled Emacs 13 | 14 | GNU网站上提供下载的Windows版Emacs是支持GnuTLS的, 不过如果你是自己编译Emacs,请参照Emacs源码目录下的 =nt/INSTALL= 的说明来编译. 15 | 16 | * Find the GnuTLS DLLs 17 | 18 | 在google上搜索"emacs gnutls windows"的结果中,第一个页面就是[[http://www.gnu.org/software/emacs/manual/html_mono/emacs-gnutls.html][这个页面]]. 上面说: 19 | 20 | #+BEGIN_QUOTE 21 | There's one way to find out if GnuTLS is available, by calling gnutls-available-p. This is a little bit 22 | trickier on the W32 (Windows) platform, but if you have the GnuTLS DLLs (available from [[http://sourceforge.net/projects/ezwinports/files/][http://]] 23 | [[http://sourceforge.net/projects/ezwinports/files/][sourceforge.net/projects/ezwinports/files/]] thanks to Eli Zaretskii) in the same directory as Emacs, you 24 | should be OK. 25 | #+END_QUOTE 26 | 27 | 在那个网页上,我找到了这个: 28 | 29 | gnutls-3.0.9-w32-bin.zip 2012-01-02 7.2 MB 30 | 31 | * Extract the GnuTLS DLLs 32 | 33 | 我一开始想用Explorer来打开zip文件,然后将文件拷贝出来,结果失败了. 文件拷贝不出来而且也没有任何错误提示. 我只好解压该zip文件然后将解压出来的所有DLL文件拷贝到Emacs安装路径的 =bin= 目录下. 在我的电脑上就是 =C:\Program Files (x86)\emacs-24.3\bin=. 34 | 35 | * Restart Emacs and try it 36 | 37 | 一切做完之后,重启Emacs并输入 =M-: (gnutls-available-p) RET=,你应该能在echo area中看到显示了一个t,这表示Emacs找到了GnuTLS库了. 38 | 39 | * Configure trust files 40 | 41 | 不过,如果你现在尝试创建一个TLS链接,会提示证书验证失败. 这时因为GnuTLS需要一些CA证书来验证它所连接的服务器. 42 | 它会在变量 =gnutls-trustfiles= 所指定的位置搜索CA证书, 不过该变量的默认值并不适用于Windows环境. 43 | 44 | 我不清楚怎么让GnuTLS使用Windows系统自带的那些证书,没办法,只能从其他地方获取证书集了. 好在cURL项目 [[http://curl.haxx.se/docs/caextract.html][提供了线程的证书集]] 可以下载. 45 | 将下载下来的 =cacert.pem= 文件放在合适的目下,然后设置 =gnutls-trustfiles= 指向它就行了. 46 | 注意,传递给GnuTLS的文件名不会被扩展,因此你不能用 =~= 来表示你的home目录,你需要使用绝对路径的文件. 47 | 48 | * See if it works 49 | 50 | 将下列代码拷贝到 =*scratch*= buffer: 51 | 52 | #+BEGIN_SRC emacs-lisp 53 | (condition-case e 54 | (delete-process 55 | (gnutls-negotiate 56 | :process (open-network-stream "test" nil "www.google.com" 443) 57 | :hostname "www.google.com" 58 | :verify-error t)) 59 | (error e)) 60 | #+END_SRC 61 | 62 | 将光标放在代码后面然后按下 =C-j=. 如果在buffer中插入一个 =nil=,则表示验证通过了,你的配置生效了. 63 | 64 | 否则你应该会看到如下错误: 65 | 66 | #+BEGIN_SRC emacs-lisp 67 | (error "Certificate validation failed www.google.com, verification code 66") 68 | #+END_SRC 69 | 70 | 如果出现这种错误,那么可以试试将变量 =gnutls-log-level= 的值设置成大于0的值,再进行调试. 71 | -------------------------------------------------------------------------------- /emacs-common/在单机上运行多个emacs守护进程.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在单机上运行多个emacs守护进程 2 | #+URL: http://tychoish.com/post/running-multiple-emacs-daemons-on-a-single-system/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2016-12-26 周一 15:55] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 10 | 我敢说肯定不止我一个人想一次性运行多个独立的emacs daemon实体. 我是基于以下情况所以才想这么作的: 11 | 12 | 我在同一台笔记本上面有很多独立的项目,每个项目都有许多打开的buffer. 这样一来我经常一次性要打开两百多个buffer. 这让我难以管理这些buffer. 13 | 更何况,当我写博客时,我并不关心我工作上的那些东西, 而当我设计网站时也跟我写小说的buffer没有任何关系. 14 | 15 | 如果我不是以daemon的方式运行emacs的话(调用"emacs --daeon"命令),我大可以直接运行几个emacs实例就行了. 16 | 然而直接运行emacs实例的问题在于,若X11崩溃的话,这些emacs实例也会跟着崩溃. 这可是个大问题. 17 | 而以daemon的方式运行emacs虽然可以避免这个问题,但一般来说你只能运行一个这样的实例,比较emacsclient如何知道你想要连接上的是哪个实例呢? 这也是个大问题. 18 | 19 | 好在最终我还是找到了解决之道. 20 | 21 | 你可以在配置文件的最上面加上这么一行: 22 | 23 | #+BEGIN_SRC emacs-lisp 24 | (setq server-use-tcp t) 25 | #+END_SRC 26 | 27 | 默认情况下,emacs daemon监听的是UNIX/system socket. 然而通过设置上面的选项,可以让它监听基于TCP的连接. 28 | I've not yet figured out how to create the required SSH tunnel to make this particularly cool, but it makes this use case possible. 29 | 30 | 然后,你可以使用下面这种格式来启动emacs: 31 | 32 | #+BEGIN_SRC shell 33 | emacs --daemon=tychoish 34 | emacs --daemon=work 35 | #+END_SRC 36 | 37 | 每个Emacs服务进程都会在"~/.emacs.d/server/"目录下创建一个状态文件. 38 | 如果你使用版本控制软件来管理你的emacs配置的话,你恐怕需要让版本控制软件忽略该目录下的文件才行了. 39 | 40 | 当运行emacs client时,使用类似下面的命令: 41 | 42 | #+BEGIN_SRC shell 43 | emacsclient --server-file=tychoish -c -n 44 | emacsclient --server-file=work -c -n 45 | #+END_SRC 46 | 47 | 当然你可以在命令后面添加要编辑的文件或者任何其他emacsclient支持的option都可以. 48 | 这些命令看起来蛮长的,因此我在zsh配置中为它们设置了别名,并且在我的窗口管理器中也设置了快捷键. 49 | 这样能很方便的在指定emacs实例上打开frame了. 50 | 51 | 我还在我的用户crontab文件中添加了下面两行的配置: 52 | 53 | #+BEGIN_SRC shell 54 | @reboot emacs --daemon=tychoish 55 | @reboot emacs --daemon=work 56 | #+END_SRC 57 | 58 | 这两行可以使得操作系统重启后自动启动 =tychoish= 和 =work= 这两个带名字的emacs守护进程. 59 | 一般来说,在crontab中设置 =@reboot= 项,可以使得那些每次重启电脑都需要执行,而启动过程又巨耗时的任务看起来更快一些. 60 | 61 | 最后,我想提供一个建议并咨询一个问题: 62 | 63 | + 建议: 虽说你可以创建大量的emacs实例,而且这些实例实际也不怎么占用内存,但是实例太多的话,你很可能会忘掉哪个buffer在哪个实例中. 64 | + 问题: 是否有办法在emacs lisp中获取server-name的值呢? 这样我就能根据不同的server-name加载不同的配置了. 65 | -------------------------------------------------------------------------------- /emacs-common/如何同时Narrow同一buffer的不同区域.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 如何同时Narrow同一buffer的不同区域 2 | #+URL: http://irreal.org/blog/?p=2602 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-09-06 周二 15:16] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | 写博客的一大乐趣在于能从评论中学习到各种知识. 之前我写过一篇关于[[http://irreal.org/blog/?p=2589][fancy-narrow]] 的博文,结果引起了一场 [[http://irreal.org/blog/?p=2589#comment-144873][Wilfred]] 与 [[http://irreal.org/blog/?p=2589#comment-144958][Phil]] 之间的争论. 10 | Wilfred对比了一下 =fancy-narrow= 与 =narrow-to-region-indirect=. 我以为 =narrow-to-region-indirect= 是一个我还不熟悉的标准命令或函数,就打算找个时间研究一下它. 结果Phil告诉我它其实是一段好用的自定义代码. 11 | 12 | =narrow-to-regioin-indirect= 的思路是,多次复制当前buffer,然后在各个克隆buffer中narrow不同region. 如果你不清楚怎么实现,Zane Ashby写过一篇很好的帖子,不仅给出了[[http://demonastery.org/2013/04/emacs-narrow-to-region-indirect/][一个使用场景]] 还给出了一个实现该功能的函数的源代码. 正如Phil所评论的,在他看到这段代码之前,他都是手工来操作的. 13 | 14 | 你可能会觉得,完全可以打开一个文件,然后将buffer分割成多份,再在不同的window上去narrow不同的region. 但是这样是行不通的,因为在其中一个buffer中narrow会同时影响到所有其他window中的buffer. 15 | 解决问题的关键在于创建[[http://www.gnu.org/software/emacs/manual/html_node/emacs/Indirect-Buffers.html#Indirect-Buffers][indirect buffers]], 这些indirect buffers可以分别narrow不会相互影响. 此外在任何indirect buffer中所做的内容修改同时会反映到其他clone bufer及基础buffer中去. 这样你修改完内容后,只需要删掉那些clone buffer然后保存基础buffer就可以了. 16 | 17 | 你可以在Ashby的博客中找到相关代码,grayswx也在[[http://paste.lisp.org/display/135818][这里]] 中实现了一个稍微有些差异的版本. 18 | 如果你需要同时操作同一buffer的不同部分,尤其当你想对不同部分使用不同mode时,不妨试试这种方法. 19 | -------------------------------------------------------------------------------- /emacs-common/如何在xubuntu16.04上构建Emacs25.1.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 如何在xubuntu16.04上构建Emacs25.1 2 | #+URL: http://www.lonecpluspluscoder.com/2016/10/08/how-to-build-gnu-emacs-25-1-on-xubuntu-16-04/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-10-14 五 06:56] 6 | #+OPTIONS: ^:{} 7 | 8 | GNU Emacs 25.1已经发布了, 是时候写一篇关于如何在新发布的Ubuntu上安装Emacs25.1的文章了. 9 | 我自己本身用的是XUbuntu 16.04, 不过该指南对其他Ubuntu版本应该也适用的. 10 | package版本虽然有所改变,但是它们的名字自我第一次发布此系列文章以来,并未有所改变. 11 | 12 | 首先要确认是否已经安装过了build-essential package. 而且为了方便,我还安装了Emacs24上的那些构建依赖的package. 13 | 这样一来我们自己构建的Emacs25.1应该与Ubuntu官方的Emacs24有了相同的构建项. 14 | 15 | #+BEGIN_SRC sh 16 | sudo apt install build-essential 17 | sudo apt-get build-dep emacs24 18 | #+END_SRC 19 | 20 | 我这里的结果是已经安装了build-essential, 但是Emacs的构建依赖并没有安装. Emacs的构建依赖于许多的package,它在我电脑上足足安装了171个新package. 21 | 22 | 与此同时, 你可以在[[https://www.gnu.org/software/emacs/download.html][Emacs下载页面]] 下载Emacs源代码,然后将其解压到一个合适的目录离去. 23 | 然后你只需要运行 =./configure= 就可以开始构建了: 24 | 25 | #+BEGIN_SRC sh 26 | ./configure --prefix=$HOME/local 27 | make && make install 28 | #+END_SRC 29 | 30 | 上面的 =-prefix= 部分表示让构建出来的东西放到我自己的local目录下. 这样一来就不会与系统安装的package所冲突了. 31 | 32 | 行了,现在可以试一下Emacs25.1是否可以用了. 下面是我的运行结果: 33 | 34 | #+BEGIN_SRC sh 35 | ~/local/bin/emacs --version 36 | # GNU Emacs 25.1.1 37 | # Copyright (C) 2016 Free Software Foundation, Inc. 38 | # GNU Emacs comes with ABSOLUTELY NO WARRANTY. 39 | # You may redistribute copies of GNU Emacs 40 | # under the terms of the GNU General Public License. 41 | # For more information about these matters, see the file named COPYING. 42 | #+END_SRC 43 | 44 | 当然,同时也会有一个图像界面的Emacs: 45 | 46 | [[https://img.readitlater.com/i/www.lonecpluspluscoder.com/wp-content/uploads/2016/10/emacs-25.1-ubuntu/RS/w704.png]] 47 | 48 | Happy Emacsing! 我该去实验一下Emacs25的新特效了. 49 | -------------------------------------------------------------------------------- /emacs-common/将粘贴板中的内容添加到Emacs的kill-ring中.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 将粘贴板中的内容添加到Emacs的kill-ring中 2 | #+URL: http://pragmaticemacs.com/emacs/add-the-system-clipboard-to-the-emacs-kill-ring/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2016-12-04 日 14:11] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 10 | 我之前写过一篇文章: [[http://pragmaticemacs.com/emacs/automatically-copy-text-selected-with-the-mouse/][如果将Emacs中选中的内容保存到粘贴板中]]. 这篇则是关于整合粘贴板与Emacs的另一篇文章. 11 | 该文的灵感来源于牛逼轰轰的[[https://github.com/dakrone/eos/blob/master/eos.org][Emacs operating system]] 系列配置文件. 该系列配置文件中包含又好多的好东西. 12 | 13 | 默认情况下,若你先拷贝了什么东西到粘贴板中(假设是从firefox中拷贝了一些文本),然后又在Emacs中拷贝或剪切了一些文本,则你会发现之前从firefox中拷贝的那些内容被覆盖了. 14 | 不过如果在你 [[http://pragmaticemacs.com/emacs/editing-your-emacs-config-file/][emacs配置文件]] 中设置了下面这个选项,则在Emacs拷贝或剪切文本时还会将粘贴板中的内容添加到kill-ring中,这样一来你就可以通过 [[http://pragmaticemacs.com/emacs/counsel-yank-pop-with-a-tweak/][遍历Emacs粘贴板历史]] 的方式找回原内容了. 15 | 16 | #+BEGIN_SRC emacs-lisp 17 | ;; Save whatever’s in the current (system) clipboard before 18 | ;; replacing it with the Emacs’ text. 19 | ;; https://github.com/dakrone/eos/blob/master/eos.org 20 | (setq save-interprogram-paste-before-kill t) 21 | #+END_SRC 22 | -------------------------------------------------------------------------------- /emacs-common/整合iTerm2与Emacs.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 整合iTerm2与Emacs 2 | #+URL: https://sam217pa.github.io/2016/09/01/emacs-iterm-integration/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-09-18 日 22:11] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | Emacs中的终端体验又慢有难用,因此我都是用iTerm2作为我的主要终端来用的. 我创建了三个emacs-lisp函数,这些函数会调用AppleScript,使得我可以快速在iTerm和spacemacs之间相互切换. 10 | 11 | 这个函数返回当前打开文件所在的目录. 若当前buffer为 =*scratch*= 或类似的buffer,则会返回home目录. 12 | 13 | #+BEGIN_SRC emacs-lisp 14 | (defun get-file-dir-or-home () 15 | "If inside a file buffer, return the directory, else return home" 16 | (interactive) 17 | (let ((filename (buffer-file-name))) 18 | (if (not (and filename (file-exists-p filename))) 19 | "~/" 20 | (file-name-directory filename)))) 21 | #+END_SRC 22 | 23 | 这个函数让我cd到当前编辑文件所在的目录. 若我处于类似 =*scratch*= buffer中则会cd到 =$HOME= 目录. 然后进入iTerm2应用. 24 | 25 | #+BEGIN_SRC emacs-lisp 26 | (defun iterm-goto-filedir-or-home () 27 | "Go to present working dir and focus iterm" 28 | (interactive) 29 | (do-applescript 30 | (concat 31 | " tell application \"iTerm2\"\n" 32 | " tell the current session of current window\n" 33 | (format " write text \"cd %s\" \n" (get-file-dir-or-home)) 34 | " end tell\n" 35 | " end tell\n" 36 | " do shell script \"open -a iTerm\"\n" 37 | )) 38 | ) 39 | #+END_SRC 40 | 41 | EDIT: 2016-09-05 我接受Steve Purcell的建议. Emacs已经有了一个名为 =default-directory= 的变量. 它的值就是当前编辑buffer所在的目录,若当前buffer没有对应的文件,则其值为nil. 因此 ~(or default-directory "~")~ 也能起到相同的作用. 42 | 43 | 为了防止目录名中包含类似unicode字符或空格这样的特殊字符, 我们需要在将其传递到shell前将其引用起来. 这可以用 =shell-quote-argument= 来实现. 44 | 不过,由于我们需要把这些引用之后的特殊字符作为参数传递到applescript语句中. 因此引用符可能会看起来比较乱. 45 | 下面这个函数即使当处于类似 “~/Users/me/Google Drive/pâte à pizza/” 这样的目录中时也能正常工作. 46 | 47 | #+BEGIN_SRC emacs-lisp 48 | (defun sam--iterm-goto-filedir-or-home () 49 | "Go to present working dir and focus iterm" 50 | (interactive) 51 | (do-applescript 52 | (concat 53 | " tell application \"iTerm2\"\n" 54 | " tell the current session of current window\n" 55 | (format " write text \"cd %s\" \n" 56 | ;; string escaping madness for applescript 57 | (replace-regexp-in-string "\\\\" "\\\\\\\\" 58 | (shell-quote-argument (or default-directory "~")))) 59 | " end tell\n" 60 | " end tell\n" 61 | " do shell script \"open -a iTerm\"\n" 62 | )) 63 | ) 64 | #+END_SRC 65 | 66 | 最后这个函数仅仅是进入iTerm2应用而不会修改工作目录. 67 | 68 | #+BEGIN_SRC emacs-lisp 69 | (defun iterm-focus () 70 | (interactive) 71 | (do-applescript 72 | " do shell script \"open -a iTerm\"\n" 73 | )) 74 | #+END_SRC 75 | 76 | 最后,我用general.el来将这两个函数的快捷键分别设置为SPC '以及SPC ?. 77 | 78 | #+BEGIN_SRC emacs-lisp 79 | (general-define-key 80 | :states '(normal visual insert emacs) 81 | :prefix "SPC" 82 | "'" '(iterm-focus :which-key "focus iterm") 83 | "?" '(iterm-goto-filedir-or-home :which-key "focus iterm - goto dir") 84 | ) 85 | #+END_SRC 86 | -------------------------------------------------------------------------------- /emacs-common/是否值得学习Emacs-GNUS--陈斌的回答.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 是否值得学习Emacs GNU--陈斌的回答 2 | #+URL: https://www.quora.com/Is-it-worth-learning-gnus-for-emacs/answer/Chen-Bin-3?srid=H9Il&share=7b1a5984 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: raw 5 | #+DATE: [2016-07-25 一 17:32] 6 | #+OPTIONS: ^:{} 7 | 8 | * 请问是否指的花时间学习Emacs GNUS呢? 9 | 10 | 目前我的所有文本编辑和代码编写工作都是用Emacs来完成的,我很喜欢Emacs. 但是在阅读新闻/博客/twitter/gmail时,我还是通过网页来进行的. 我曾经稍微试用过一下子gnus, 但是在我花时间深入学习gnus之前,我想了解下我的回报有哪些---GNUS看起来相当复杂而且它的价值主张也不是很清晰. 11 | 12 | * 陈斌的回答 13 | 14 | 值得花时间, 你可以看看我写得[[http://blog.binchen.org/?p=403][Gnus玩转Gmail实战]]指南 15 | 16 | 使用Gnus的原因有: 17 | ================= 18 | 1. 发送email不再困难,在任何地方按下 =C-x m= 就能发送一封新邮件 19 | 2. 将所有邮件标记为已读的操作要比在Gmail web UI上操作更来得方便 20 | 3. 添加附件和转发多个email真的很方便 21 | 4. 当使用email时可以借力于其他的emacs插件 22 | 23 | 请注意以下几点: 24 | ========= 25 | 1. 我并不阅读新闻组和邮件列表 26 | 2. 有时候我也会用Gmail web UI 27 | 3. 通过整合w3m,Gnus也支持阅读html邮件 28 | 4. 通过整合org-mode 和 org-mime这两个插件,你可以编写html邮件(当在编写包含html表格的email时特别棒). 29 | 30 | 使用Gnus的关键点 31 | ================== 32 | 这个部分很重要,因为即使你已经被我说服了使用Gnus. 但是尝试过一次后,你依然很可能会放弃. 33 | 34 | Gnus默认情况下,在某些情况下会自动隐藏一些电子邮件和新闻组,这简直是自作聪明. 大多数人无法忍受Gnus是因为他们不知道为什么有些邮件被丢失掉了. 35 | 36 | 例如. 你阅读完了所有的邮件后就没有再管它们了. 然后一个小时后,你的老板告诉你,其中有一封邮件特别重要,他要和你一起讨论一下这封邮件. 你打开Gnus,结果发现 =INBOX= folder不见了! 37 | Gnus默认会隐藏不包含未读邮件的folder! 随便一提,Gmail其实也有类似的功能,但是默认是关闭的. 38 | 39 | 如果你带着疑问来阅读manual(我就是这么做的,我花了整整5个小时来阅读manual), 你会发现它的manual有点难以理解. 40 | 41 | 不管怎么说,我费了老大劲总算是读完了manual了,现在我已经知道该怎么做了. 42 | 43 | 关键点在于告诉gnus *显示出所有的东西*: 44 | 1. 看我上面指南,上面有详细的配置信息 45 | 2. 当你打开没有未读邮件的 =INBOX= folder时,通过 =C-u = 打开而不要直接按 == 打开,这样才能看到所有的电子邮件. 46 | 47 | Tips 48 | ==== 49 | 1. 当每天都要编写相似内容的邮件时(例如,回复工作日程),yasnippet很有帮助. 50 | 2. 按下 =c= 就能将inbox中的所有邮件都标记会已读,这真是太方便了(相对来说,Gmail的界面就差了点) 51 | 3. 搜索,过滤(在gnus中,这些动作统称为限定(limit))电子邮件的操作令人满意 (搜索没有gmail那么方便,但是过滤要比gmail方便多了) 52 | 4. 可以安装一个名为 =popfile= 的软件来为你的邮件分类,它比google自己的分类器要好太多了(不管是在过滤规则上,还是自动加标签上都是这样) 53 | 5. 在Gnus为email添加附件很有效率(只需要1,2秒就能搞定) 54 | 6. 转发邮件或者将email作为附件(attach email)都很容易 55 | 7. 你可以使用另一个叫做 =davmail= 的软件来从outlook exhange server中收取邮件(请使用pop3协议而不是imap协议,因为gnus有些bug),当然若你使用POP3协议,别忘了在server端留一份邮件的拷贝. 56 | 8. 阅读html邮件也很容易,使用emacs-w3m就行 57 | 9. 使用org-mine编写html邮件要比在其他软件中更有效率,尤其要创建html表格时,效率快了十倍有余. 58 | 59 | 若你需要详细的配置信息,请查看[[http://github.com/redguardtoo/emacs.d][redguardtoo/emacs.d]] 60 | -------------------------------------------------------------------------------- /emacs-common/更好的compile命令.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 更好的compile命令 2 | #+URL: http://endlessparentheses.com/better-compile-command.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-06-18 六 22:38] 6 | #+OPTIONS: ^:{} 7 | 8 | 运行compile之前都要确认是否保存未保存的buffer,以及没有为compile分配一个默认的快捷键,这两个设定简直让人无语. 下面这段代码能够修正这两个问题,并且还能使得编译窗口遵照预定的大小显示,同时光标还会随着输出的增加而下移. 9 | #+BEGIN_SRC emacs-lisp 10 | ;; This gives a regular `compile-command' prompt. 11 | (define-key prog-mode-map [C-f9] #'compile) 12 | ;; This just compiles immediately. 13 | (define-key prog-mode-map [f9] 14 | #'endless/compile-please) 15 | 16 | ;; I'm not scared of saving everything. 17 | (setq compilation-ask-about-save nil) 18 | ;; Stop on the first error. 19 | (setq compilation-scroll-output 'next-error) 20 | ;; Don't stop on info or warnings. 21 | (setq compilation-skip-threshold 2) 22 | 23 | (defcustom endless/compile-window-size 105 24 | "Width given to the non-compilation window." 25 | :type 'integer 26 | :group 'endless) 27 | 28 | (defun endless/compile-please (comint) 29 | "Compile without confirmation. 30 | With a prefix argument, use comint-mode." 31 | (interactive "P") 32 | ;; Do the command without a prompt. 33 | (save-window-excursion 34 | (compile (eval compile-command) (and comint t))) 35 | ;; Create a compile window of the desired width. 36 | (pop-to-buffer (get-buffer "*compilation*")) 37 | (enlarge-window 38 | (- (frame-width) 39 | endless/compile-window-size 40 | (window-width)) 41 | 'horizontal)) 42 | #+END_SRC 43 | 44 | 45 | * Update 16 Jun 2015 46 | 47 | 感谢Clément及abo-abo的宝贵意见:他们都告诉我有个叫 =compilation-skip-threshold= 的变量. 通常情况下Warnings也是需要修复的,但是再某些语言中,很可能你根本无法修复那些warning, 因此编译窗口最好不要因为出现warnning就停止滚动. 48 | 49 | * Update 17 Jun 2016 50 | 51 | 修改 =endless/compile-please= 为会传递prefix参数到 =compile= 函数, 这样若使用prefix参数调用该函数,则会再comint-mode下编译(这样你可以与编译buffer进行交互了). 详情请参见[[http://endlessparentheses.com/provide-input-to-the-compilation-buffer.html][这篇博文]] 的UPDATE部分. 52 | -------------------------------------------------------------------------------- /emacs-common/更改Emacs的字体显示.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 更改Emacs的字体显示 2 | #+URL: http://jjasghar.github.io/blog/2017/01/04/changing-fonts-in-emacs/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2017-02-22 周三 12:50] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 我老是忘了怎么配置字体. 就此记录一下. 10 | 11 | 首先,要知道你现在用的字体信息,只需要在 =*scratch*= buffer 中输入并执行: 12 | 13 | #+BEGIN_SRC emacs-lisp 14 | (face-attribute 'default :font) 15 | #+END_SRC 16 | 17 | 就会你的 minibuffer 中显示字体信息了. 我建议你把这个默认值从 =*Messages*= buffer中提取出来放到 =*scratch*= buffer中并注释起来. (你在设置字体的时候可能会用到) 18 | 19 | 然后我们可以设置默认字体了, 在 =*scratch*= buffer中执行以下语句: 20 | 21 | #+BEGIN_SRC emacs-lisp 22 | (set-face-attribute 'default nil :font "-outline-Monaco-normal-normal-normal-mono-16-*-*-*-c-*-iso8859-1" ) 23 | #+END_SRC 24 | 25 | 会发现字体已经发生改变了, 而且根据 [[https://www.emacswiki.org/emacs/SetFonts][wiki]] 上说的,这种方法“…不仅改变了当前frame的默认字体,而且也会影响后面打开的frame…” 26 | 27 | 如果你不想更改当前frame的字体,那么运行 28 | 29 | #+BEGIN_SRC emacs-lisp 30 | (set-face-attribute 'default t :font "-outline-Monaco-normal-normal-normal-mono-16-*-*-*-c-*-iso8859-1" ) 31 | #+END_SRC 32 | 33 | 而 [[https://www.reddit.com/r/emacs/comments/5m0nig/notes_on_how_to_change_fonts_in_emacs_without/][reddit]], [[https://www.reddit.com/user/eli-zaretskii][eli-zaretskii]] 的建议是用: 34 | 35 | #+BEGIN_SRC emacs-lisp 36 | (add-to-list 'default-frame-alist '(font . "your-font-name-here")) 37 | #+END_SRC 38 | 39 | 我试了一下,确实有用,只是似乎对当前buffer没有效果. 40 | 41 | 用这种方法,你就可以不用重启就能实验不同字体的显示笑过了. 42 | 43 | 另外说明一下,字体信息中的 =16= 指的是字体大小,我个人觉得 =20= 会好一点,不过这并不重要. 44 | 45 | 如果你想尝试一下其他字体,可以看看 [[https://www.emacswiki.org/emacs/GoodFonts][这里]]. 46 | 47 | 一旦你找到了想要的字体,就把该配置加到 =init.el= 中去吧,这样每次启动emacs都会用新字体显示了. 48 | -------------------------------------------------------------------------------- /emacs-common/现在可以在Emacs中,访问你的Google云端硬盘了.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 现在可以在 Emacs 中,访问你的 Google 云端硬盘了 2 | #+URL: https://emacsnotes.wordpress.com/2018/07/08/your-google-drive-now-within-your-emacs/ 3 | #+AUTHOR: cutefrank 4 | #+TAGS: emacs-common 5 | #+DATE: [2018-08-30 四 11:47:05 HK] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 你在使用 =Emacs 26= 吗?如果是,你可以在 Emacs 中访问 =Google 云端硬盘= 了。你需要记住的是文件名语法,类似于 10 | =/gdrive:john.doe@gmail.com:/path/to/file= 。加载后,你的远程 =Google 云端硬盘= 操作与任何其他本地文件夹相同, 11 | 你可以使用 =dired= 命令对其进行操作。 12 | 13 | 这是设定的步骤: 14 | 15 | * 将你的 Google 帐户添加到 GNOME Desktop[[https://help.gnome.org/users/gnome-help/stable/accounts.html.en][1]] 16 | 17 | [[https://emacsnotes.files.wordpress.com/2018/07/screenshot-from-2018-07-08-08-58-36.png?w=740]] 18 | 19 | * 确保 =文件管理器= 显示你的 =Google 云端硬盘= 20 | 21 | [[https://emacsnotes.files.wordpress.com/2018/07/screenshot-from-2018-07-08-09-00-08.png?w=740]] 22 | 23 | * 使用 `/gdrive:john.doe@gmail.com:/` 打开你的 =Google 云端硬盘= 24 | 25 | [[https://emacsnotes.files.wordpress.com/2018/07/screenshot-from-2018-07-08-09-34-18.png?w=740]] 26 | 27 | * 你可以在 Emacs 中,访问你的 =Google云端硬盘= 了 28 | 29 | [[https://emacsnotes.files.wordpress.com/2018/07/screenshot-from-2018-07-08-09-36-571.png?w=740]] 30 | -------------------------------------------------------------------------------- /emacs-common/用Emacs作展示.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 用Emacs作展示 2 | #+URL: http://nathanielknight.ca/articles/emacs_presentation.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-09-07 周三 16:05] 6 | #+OPTIONS: ^:{} 7 | 8 | 我基本上属于是那种[[http://c2.com/cgi/wiki?EmacsAsOperatingSystem][什么事都想用Emacs来完成的人]], 最近我打造除了一套独有的Org Mode展示工具. 9 | 其实这套东西并不复杂:它引入了两个函数, =org-next-slide= 及 =org-prev-slide=, 我将它们的快捷键分别设置为 =PgUp= 与 =PgDown= (Emacs的键码分别对应 == 及 ==). 10 | 它还会将文本放大同时缩小左边距的空隙. 11 | 12 | #+BEGIN_SRC emacs-lisp 13 | (defun org-next-slide () ;; move forward to the next top-level heading 14 | (interactive) 15 | (beginning-of-buffer) ;; navigate to the beginning of this header 16 | (forward-char) 17 | (widen) ;; expand to see the whole buffer 18 | (if (search-forward-regexp ;; if we can find another header, 19 | (rx line-start "* ") nil t) 20 | (progn 21 | (org-narrow-to-subtree) ;; go to it, narrow to it, 22 | (show-all)) ;; and show all its sub-trees 23 | (progn 24 | (org-narrow-to-subtree) ;; otherwise let the user know we're 25 | (beginning-of-buffer) ;; on the last slide 26 | (message "Last slide")))) 27 | 28 | (defun org-prev-slide () ;; as `org-next-slide`, but searching backwards 29 | (interactive) 30 | (beginning-of-buffer) 31 | (widen) 32 | (if (search-backward-regexp 33 | (rx line-start "* ") nil t) 34 | (progn 35 | (org-narrow-to-subtree) 36 | (show-all)) 37 | (progn 38 | (org-narrow-to-subtree) 39 | (beginning-of-buffer) 40 | (message "First slide")))) 41 | 42 | (defvar org-slides-mode-keymap ;; bind functions to page-up and page-down 43 | (let ((map (make-sparse-keymap))) 44 | (define-key map (kbd "") 'org-next-slide) 45 | (define-key map (kbd "") 'org-prev-slide) 46 | map)) 47 | 48 | (define-minor-mode org-slides-mode ;; Synthesize into a minor mode 49 | "View org-mode sub-trees as slides." 50 | :lighter " Slides" 51 | :keymap org-slides-mode-keymap 52 | (progn 53 | (set-face-attribute 'default nil :height 300) ;; big font 54 | (set-variable 'left-margin-width '2 t) ;; little margin 55 | (set-window-buffer (selected-window) (current-buffer)))) ;; don't change windows 56 | #+END_SRC 57 | 58 | Emacs的[[http://www.gnu.org/software/emacs/manual/html_node/emacs/Narrowing.html][narrow-mode]] 可以只显示你关注的那部分文档内容. 这套工具就借助narrow-mode的这个特性,一次只展示一个 =top-level heading=,结果就好像slides一样! 59 | 60 | 用Emacs作展示的想法来自于我最近在 [[http://www.meetup.com/Vancouver-Functional-Programmers/][Vancouver函数式编程会议]] 上作的一场关于Emacs Lisp的演示. 当时主要是想使用Org Mode中[[http://orgmode.org/manual/Working-With-Source-Code.html][code-blocks]] 的功能. 61 | =Code blocks= 允许你直接在Org文件中高亮显示代码,还能跳到一个临时buffer上用恰当的mode在平常的编程环境中修改代码. 62 | 63 | =Org Slides Mode= 也有它的弱点: 它不支持显示图片与动画,只能显示出文本而已. 64 | 而且你还需要手工[[http://practicaltypography.com/presentations.html][调整配色]]. 65 | 同时还没有备注,也不支持远程操控(这一点也许只要设置好快捷键就能搞定,只是麻烦了点). 66 | 67 | 不过一场展示中最核心的部分是现场编码(live-coding),交叉式的现场编码(interleaving living code),以及普通文本说明,这些东西用这种方式的展示效果还蛮不错的. 至于如何运用这些能力来做一场生动的演讲就看你的了. 68 | 69 | Nat Knight — 2015-10-10 70 | -------------------------------------------------------------------------------- /emacs-common/用Emacs编辑yaml文件.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 用Emacs编辑yaml文件 2 | #+URL: http://blog.chmouel.com/2016/09/07/dealing-with-yaml-in-emacs/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-09-23 五 06:38] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | 想当年,在90年代后期,那会儿我才开始编程,XML风靡一时. 人们把它当成是灵丹妙药,从数据存储到数据展现与处理,到处都是XML. 10 | 然而正如Joel Spolski所指出的,人们 [[http://discuss.fogcreek.com/joelonsoftware/default.asp?cmd=show&ixPost=953][最终发现]] 虽然它尝试将复杂度限制的对普通人来说可接受的程度,但是也使得人们在写标签时变得很繁琐,毕竟 =<= 和 =>= 在qwerty键盘上相距甚远. 11 | 12 | 进入新纪元后,web开始风行,到处流行起了"web services",结果人们发现XML并没有当初想的那么好用. 于是开始使用一种称为Json的格式替代XML,Json可以以跟XML类似的方式实现计算机间的相互通讯. 13 | 14 | 不过人们又发现,json也不是那么适合于web services之间通讯,毕竟它本来是设计用来在编程语言之间序列化对象用的. 而且面对那一堆的 ={}= =[]= 括号,也挺麻烦的. 15 | 16 | 最终,随着标签语言的普及,yaml横空出世了. 17 | 18 | 大多数新开发的软件都开始使用yaml, 所有的containers software ecosystem都使用yaml来进行配置. 因此,很多时候你都必须要与yaml打交道. 19 | 20 | 我对yaml无所谓喜不喜欢, 我只知道,当yaml会随着体积的增长而变得不可读. 你根本分不清哪些block是属于哪一个的,也搞不清楚要添加多少缩进才能与800行之外的block对齐. 21 | 22 | 这简直让我觉得抓狂. 由于我经常要写一些巨大的kubernetes/OpenShift yaml 文件,我经常要花大把的时间来搞清楚到底要缩进多少. 23 | 24 | 有人可能会说,你不是也用python吗,python也是用tab区分层次的呀. 是的,我用python有10年了,而这也没有对我造成什么困惑. 但这是因为:第一,我从不会写超过5000行的函数; 第二,我Emacs的python mode精心配置过了,能帮我自动解决这个问题. 25 | 26 | 终于讲到正题了, 我们需要好好地配置一下编辑器才行. 下面就讲讲我是如何配置Emacs,使得编辑这么巨大的yaml文件变得可以忍受. 27 | 28 | 闲话不多说, 下面这些emacs扩展都是我认为可以帮助编写yaml的: 29 | 30 | [[https://github.com/antonj/Highlight-Indentation-for-Emacs][Highlight Indentation for Emacs]] 31 | 32 | [[http://i0.wp.com/blog.chmouel.com/wp-content/uploads/2016/09/2016-09-07__09-06-21-543.png]] 33 | 34 | 该mode会用一个bar来显示当前的缩进. 35 | 36 | [[https://github.com/hbin/smart-shift][Smart Shift]] 37 | 38 | [[http://i0.wp.com/blog.chmouel.com/wp-content/uploads/2016/09/t.gif]] 39 | 40 | 该mode不会为你显示缩进,但是可以让你缩进一段文字变得简单. 默认情况下,你需要使用命令 =Control-C Tab= 来缩进一段文字,还需要在命令前加个数字的前缀参数. 41 | 举个例子来说, =C-u 4 Control-C Tab= 会让一段文字缩进4个空格. 而Smart shift会极大的简化这一过程. 42 | 43 | [[http://flycheck.org][Flycheck-mode]] 44 | 45 | [[http://i0.wp.com/blog.chmouel.com/wp-content/uploads/2016/09/2016-09-07__09-24-14-5881.png]] 46 | 47 | 当你在编程时,不管是什么语言,都应该开启该功能, 该插件会用ruby-yaml库来校验你的yaml文件并显示出错误的地方. 48 | 49 | [[https://stackoverflow.com/questions/1587972/how-to-display-indentation-guides-in-emacs/4459159#4459159][aj-toggle-fold]] 50 | 51 | [[http://i1.wp.com/blog.chmouel.com/wp-content/uploads/2016/09/2016-09-07__09-36-55-32078.png]] 52 | 53 | 该函数是我从stackoverflow上找到的,它的作者同时也是 =Highlight-Indentation-for-Emacs= 的作者. 这个函数的作用就是折叠起所有比当前行更缩进的代码. 是一个用来显示当前文件大纲的好工具. 54 | -------------------------------------------------------------------------------- /emacs-common/禁止Emacs将package-selected-package变量写入初始化文件中.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 禁止Emacs将package-selected-package变量写入初始化文件中 2 | #+URL: http://irreal.org/blog/?p=5630 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-10-11 周二 11:52] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | 升级到Emacs 25.1后,Emacs会保存一个名为 =package-selected-packages= 的变量,这个变量保存了你选定的package的列表. 10 | 在删除package时,Emacs会用这个变量来处理依赖关系,同时也使得你能将package配置迁移到多台机器上.This is used to handle dependencies on 11 | 12 | 但是这又带来了一个问题: Emacs会将这个变量下到你初始化文件(init.el或.emacs)的custom section里面. 这意味着要同步(比如通过git进行同步)各个机器上的配置变得更加困难了. 13 | 而且我也不希望由Emacs自动为我生成配置信息,我希望我的配置文件能由我完全掌握. 14 | 15 | 幸好,[[https://www.reddit.com/r/emacs/comments/53zpv9/how_do_i_get_emacs_to_stop_adding_custom_fields/][解决这个问题的方法有很多]]. 我选择了其中一个最简单的方法,那就是为Emacs自动生成的配置信息指定一个独立的配置文件. 16 | 你选择在初始化文件中是否加载这个文件都没有问题,但至少Emacs不会再搞乱你的初始化文件了. 17 | 当然还有其他的解决方案,详情请查看刚才那个链接里的内容. 18 | -------------------------------------------------------------------------------- /emacs-common/简单几步将Emacs打造成为C++_IDE.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 简单几步将Emacs打造成为C++_IDE 2 | #+URL: http://blog.binchen.org/posts/emacs-as-c-ide-easy-way.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-08-30 周二 17:27] 6 | #+OPTIONS: ^:{} 7 | 8 | 我发明了一种快速又友好的解决方案. 9 | 10 | 该解决方案适用于Linux/OSX/Cygwin (应该也适用于Windows环境,但我不在Windows下作开发所以不确定). 11 | 12 | 只有很少量的配置. 你只需要安装 =GNU Global= 以及另外两个Emacs插件: 13 | 14 | + [[https://github.com/company-mode/company-mode][company]] ,用于代码补全 15 | + [[https://github.com/syohex/emacs-helm-gtags][emacs-helm-gtags]] 或 [[https://github.com/syohex/emacs-counsel-gtags][emacs-counsel-gtags]] 用于代码导航. 我个人用的是 =emacs-counsel-gtags=. 16 | 17 | 下面是步骤说明. 18 | 19 | * 步骤1, 创建一个示例项目来作实验 20 | 21 | 假设我有两个目录 =~/proj1= 以及 =~/proj2=. 两个项目都会用到第三方库的C++头文件,这些头文件放在只读目录 =/usr/include= 中. 22 | 23 | 新建一个 =~/obj= 目录来存放第三方库的索引文件. 24 | 25 | #+BEGIN_SRC sh 26 | mkdir -p ~/{proj1,proj2,obj} 27 | #+END_SRC 28 | 29 | =~/proj2/lib.cpp= 的内容为: 30 | 31 | #+BEGIN_SRC cpp 32 | void proj2_hello(int a2, char* b2) { 33 | } 34 | #+END_SRC 35 | 36 | =~/proj1/main.cpp= 的内容为: 37 | 38 | #+BEGIN_SRC cpp 39 | void proj1_hello(int a1, char* b1) { 40 | } 41 | 42 | int main(int argc, char *argv[]) { 43 | return 0; 44 | } 45 | #+END_SRC 46 | 47 | * 步骤2, 扫描C++代码并配置Emacs 48 | 49 | 在shell中运行下列代码来扫描代码Run below command in shell to scan code, 50 | 51 | #+BEGIN_SRC sh 52 | cd /usr/include && MAKEOBJDIRPREFIX=~/obj gtags -O && cd ~/proj1 && gtags && cd ~/proj2 && gtags 53 | #+END_SRC 54 | 55 | 设置好相应的Emacs插件后(最简单的方法只需要把这些插件安装好就行了), 将下列代码插入到 =~/.emacs= 中, 56 | 57 | #+BEGIN_SRC emacs-lisp 58 | ;; Please note `file-truename' must be used! 59 | (setenv "GTAGSLIBPATH" (concat "/usr/include" 60 | ":" 61 | (file-truename "~/proj2") 62 | ":" 63 | (file-truename "~/proj1"))) 64 | (setenv "MAKEOBJDIRPREFIX" (file-truename "~/obj/")) 65 | (setq company-backends '((company-dabbrev-code company-gtags))) 66 | #+END_SRC 67 | 68 | * 使用方法 69 | 70 | 就像平常那样使用Emacs就行了. 71 | 72 | 不过有一点要逐一,你需要安装8月25日之后编译的company,因为我昨天才[[https://github.com/company-mode/company-mode/issues/570][修复了一个company的issue]]. 73 | 74 | [[http://blog.binchen.org/wp-content/cpp-gtags-demo-nq8.png]] 75 | 76 | 77 | * 技术细节(选读) 78 | 79 | 通过查阅 [[https://www.gnu.org/software/global/manual/global.html][GNU Global manual]] 来搞清楚环境变量 =GTAGSLIBPATH= 和 =MAKEOBJDIRPREFIX= 的意思. 80 | -------------------------------------------------------------------------------- /emacs-common/自动拷贝鼠标选中的文本.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 自动拷贝鼠标选中的文本 2 | #+URL: http://pragmaticemacs.com/emacs/automatically-copy-text-selected-with-the-mouse/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-10-08 六 07:40] 6 | #+OPTIONS: ^:{} 7 | 8 | 我不经常使用鼠标,但是我的一个同事,他在OS X上使用Emacs,就很希望能够做到像X11上那样用鼠标选中的文本自动复制到系统剪贴板上(linux用户应该对此很熟悉). 9 | 要实现这样其实蛮简单的,只需要添加下行到你的[[http://pragmaticemacs.com/emacs/editing-your-emacs-config-file/][emacs配置文件中]]: 10 | 11 | #+BEGIN_SRC emacs-lisp 12 | (setq mouse-drag-copy-region t) 13 | #+END_SRC 14 | 15 | [[http://pragmaticemacs.com/emacs/look-up-help-for-keys-and-functions/][改变来的说明如下]] (C-h v mouse-drag-copy-region) 16 | 17 | #+BEGIN_QUOTE 18 | If non-nil, copy to kill-ring upon mouse adjustments of the region. 19 | #+END_QUOTE 20 | 21 | 也就是说高亮选中一段文本,这段文本就会拷贝到kill-ring(Emacs内部的剪贴板)中. 在Mac中该文本同时也会拷贝到系统剪贴板上这样你就可以复制到其他app中了. 22 | -------------------------------------------------------------------------------- /emacs-common/获取Emacs版本信息的正确方式.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 获取Emacs版本信息的正确方式 2 | #+URL: http://mbork.pl/2017-01-30_Current_Emacs_version 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2017-02-22 周三 14:16] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 当向 Emacs devs 报告bug时, 你需要告之当前使用Emacs的版本好. 通常我们使用 =M-x emacs-version= 在minibuffer中显示版本信息(或者给它带上一个prefix argument让它在当前位置插入版本信息). 10 | 然而,如果你的Emacs是直接从源代码便以过来的开发版,这个版本信息基本没啥用 - 它并不能告诉我们你用的emacs源代码来自于那一次的提交. 11 | 我把这个问题在邮件列表中咨询了一下,结果被告之其实有个 =emacs-repository-get-version= 函数能够返回当前版本库的提交信息(也就是Git的commit hash了). 12 | 13 | 由于我是不是都要报告一下Emacs bug,所以我在 =init.el= 中定义了这个函数: 14 | 15 | #+BEGIN_SRC emacs-lisp 16 | (defun insert-debug-version-info () 17 | "Insert version of Emacs and 7 characters of the commit hash." 18 | (interactive) 19 | (insert 20 | (format "GNU Emacs %s (commit %s)" 21 | emacs-version 22 | (substring (emacs-repository-get-version) 0 7)))) 23 | #+END_SRC 24 | -------------------------------------------------------------------------------- /emacs-common/调整Emacs中文本的字体大小.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 调整Emacs中文本的字体的大小 2 | #+URL: http://irreal.org/blog/?p=4823 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-07-10 日 07:46] 6 | #+OPTIONS: ^:{} 7 | 8 | 该文由 [[http://irreal.org/blog/?author=2][jcs]] 发布于 [[http://irreal.org/blog/?p=4823][2015年12月25日]] 9 | 10 | 不知道为啥,我总是记不住这个. 若你也总是记不住,把本文当成是我献给你的圣诞节礼物吧. 11 | 12 | #+BEGIN_QUOTE 13 | [[https://twitter.com/hashtag/Emacs?src=hash][#Emacs]] of the day: 在桌面演示代码? 按下C-x C-+会增加字体大小,然后重复按 C-+ 或 C-- 会增加/缩小字体大小, 按C-x C-0会恢复字体大小 14 | 15 | — Robin Green (@fatlimey) [[https://twitter.com/fatlimey/status/638494771128897541][August 31, 2015]] 16 | #+END_QUOTE 17 | 18 | 然而Green没有说明的是,你再第一次调用更改字体大小的命令后,前面的 =C-x= 就可以省略了. 因此按下 =Ctrl+x Ctrl++ Ctrl++= 会增加字体大小两次. 19 | -------------------------------------------------------------------------------- /emacs-common/通过ivy与ag实现快速multiediting.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 通过ivy与ag实现快速multiediting 2 | #+URL: https://sam217pa.github.io/2016/09/11/nuclear-power-editing-via-ivy-and-ag/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-09-23 五 06:40] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | 有时候,我需要用到一些全项目共用的变量和函数定义. 当然我需要给它们取个名字. 10 | 11 | 然而我实在是不善于取名. 12 | 13 | 所以经常我会想到一个更合适的名字,然后需要在全局范围更换这个名字, 也就是当前项目中所有用到改名字的文件都要被修改一遍. 14 | 15 | 我曾经听Fabien Dubosson(spacemacs的主要开发者之一)提到过的所谓[[https://gitter.im/syl20bnr/spacemacs?at=573d831c0cb634927f80545e][“multi-editing的核武器"]] 的编辑方法. 16 | 当我从Helm切换到Ivy后,我一直在寻找一种能重现该方法的方式. 17 | 18 | 还好,自Ivy [[http://oremacs.com/2016/04/26/ivy-0.8.0/][0.8]] 后,通过 =Ivy-occur= 与 =Wgrep= 的配合能重现该编辑方法. 19 | 20 | * Nuclear-weapon multi-editing : 21 | 22 | 我首先调用 =counsel-ag= 来在当前目录搜索要修改的字符串. 默认情况下, ag(以及counsel-ag)会跳过git忽略的那些文件. 23 | 24 | [[https://s12.postimg.org/u0co6huct/first_step.png]] 25 | 26 | 然后,我在搜索结果中按下 =C-c C-o= (ivy-occur). 这会打开一个 ivy-occur buffer. 27 | 28 | [[https://s10.postimg.org/ssiucf35l/second_step.png]] 29 | 30 | 跟着,我切换到这个buffer,然后按下 =C-x C-q= (ivy-wgrep-change-to-wgrep-mode) 来编辑该buffer. 现在我可以使用普通的搜索与替换的方法来全局修改变量名称了. 31 | 我个人喜欢用Evil-ex 命令 ~:%s/pattern/replace/g~, 当然你也可以用其他的修改方式(听说用Iedit也不错...). 32 | 33 | [[https://s14.postimg.org/s6ahj1eq9/third_step.png]] 34 | 35 | 最后我按下 =C-c C-c= (wgrep-finish-edit). 现在所有文件中的旧名称都被替换掉了. 36 | 37 | Ivy真好用. 38 | -------------------------------------------------------------------------------- /emacs-common/重新发现普通Emacs的文本编辑能力.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 重新发现普通Emacs的文本编辑能力 2 | #+URL: https://bendersteed.gitlab.io/post/rediscovering-vanilla-emacs-text-editing/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2019年 05月 17日 星期五 11:19:54 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 作为一名全职Emacs用户,我已经习惯了使用社区提供的众多包,它们极大地增强了Emacs的文本编辑功能。 9 | 有一个说法是Emacs所缺的是一个好的编辑器,然而真的是这样吗? 10 | 下面是我最近使用内置功能替代的包。 11 | 12 | * [[https://github.com/magnars/expand-region.el][expand-region.el]] - 默认标记快捷键 13 | :PROPERTIES: 14 | :CUSTOM_ID: expand-region-dot-el-default-marking-keybindings 15 | :END: 16 | 17 | Expand region是Magnar Sveen创造的一个很棒的包。他因为dash.el, s.el和multiple-cursor.el而出名。 18 | 这个包确实非常有用,因为它通过扩展区域的语义来创建区域。所以在实践中,它可以标记一个单词,一个句子,一个段落,等等。 19 | 20 | 问题是,我需要不断地扩展这个地区,直到它得到我想要的东西,这让我觉得麻烦。所以我决定花时间学习默认的Emacs的标记系统,它似乎更适合我: 21 | 22 | 1. M-@标记一个单词 23 | 2. M-h标记一个段落 24 | 3. C-M-@标记一个balanced表达式或s-表达式 25 | 4. C-M-h标记一个函数(顶级定义的) 26 | 5. C-x h标记整个buffer 27 | 28 | Since these semantics are enough for my needs, and the keybindings are somewhat consistent with the M- works on word level, C-M- on sexps and defuns paradigm of moving keybindings I am quite happy with them. 29 | 因为这些语义已经足够满足我的需要,而且这些快捷键与我已经熟悉的移动命令的快捷键有一致性,M- 是操作单词级别,C-M-是操作在表达式和函数上 30 | 31 | * [[https://github.com/joaotavora/yasnippet][yasnippet]] - Abbrev 32 | :PROPERTIES: 33 | :CUSTOM_ID: yasnippet-abbrev 34 | :END: 35 | 36 | All in all since my use of snippets is not that extensive, Abbrev mode seems to cover my needs almost fully. 37 | Yasnippet 已经大名鼎鼎了,它可以用来创建几乎任何东西的模板。原生Emacs对该问题的解决方案是Abbrev。 38 | 虽然不像Yasnippet 那样强大(Yasnippet的主要优势是你可以在扩展后控制光标的位置,这一点Abbrev做不到) 然而,Abbrev提出了一个非常适合我自己的工作流程。 39 | 如果你发现某些内容一直在重复,你可以立即通过 =C-x a g= 将其保存为一个全局缩写或 =C-x a l= 将之保存为一个局部缩写(由buffer所在的major-mode决定)。 40 | 总而言之,由于我不常用代码片段,Abbrev模式似乎完全满足了我的需求。 41 | 42 | * 所有折叠模式 -- Hideshow 43 | :PROPERTIES: 44 | :CUSTOM_ID: any-folding-mode-hideshow 45 | :END: 46 | 47 | 我还没有真正尝试过Emacs的任何一种折叠模式,主要是我想尝试某些模式时发现了与Emacs捆绑在一起的hs模式。 48 | hideshow 可以折叠大多数编程语言中的块,只要这些语言的开始/结束块的结构合理。 49 | 它工作得很好,可以通过 =M-x hs-mino-mode= 启用它,然而它的快捷键非常的糟糕。即使按照Emacs的标准,它们也很糟糕: 50 | 51 | 1. C-c @ C-c 切换显示块。 52 | 2. C-c @ C-a 现实所有. 53 | 3. C-c @ C-t 隐藏所有. 54 | 55 | 如果你碰巧在尝试hs模式,帮你自己一个忙,把这些快捷键改掉!还可以通过S-mouse-2(通常是指中键单击)切换块。 56 | 57 | * 有趣的发现 58 | :PROPERTIES: 59 | :CUSTOM_ID: interesting-discoveries 60 | :END: 61 | 62 | 我最近发现了一些有用的功能: 63 | 64 | 1. C-x C-o 删除多余的连续空白行,只保留一个空行,整齐! 65 | 2. M-s M-w 在Web中搜索标记区域的内容。 66 | 3. M-$ checks the word's under point spelling, or checks a region if there is one. 67 | 4. C-x C-t 互换两个balanced expressions。 68 | 5. 你可以用C-x C-l或C-x C-u来改变一个区域的大小写。 69 | 6. M-% (query-replace) also works in a region there is one, else works on the whole buffer. 70 | 6. M-% (query-replace) 当标记了一个区域时作用于这个区域,否则适用于整个缓冲区。 71 | 7. 通过运行M-x studlify-buffer,你总是可以成为一个14岁的前卫少年。 72 | -------------------------------------------------------------------------------- /eww/TO EWW OR NOT TO EWW.org: -------------------------------------------------------------------------------- 1 | #+TITLE: TO EWW OR NOT TO EWW 2 | #+URL: http://pragmaticemacs.com/emacs/to-eww-or-not-to-eww/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: emacs-common 5 | #+DATE: [2017-06-14 三 15:41] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 最近版本的Emacs中搭载了一款颇为不错的文本web浏览器,叫做 eww. [[https://github.com/dakrone/eos/blob/master/eos-web.org][这里]] 有一个很不错的配置例子. 我自己有时候会使用 eww, 但有时候又想用全图形的web浏览器(我亲爱的Firefox). 10 | 11 | 我将eww设置成了Emacs中的默认浏览器: 12 | #+BEGIN_SRC emacs-lisp 13 | (setq browse-url-browser-function 'eww-browse-url) 14 | #+END_SRC 15 | 16 | 但同时,我也创建了一些包装函数,以防有时候我想用系统默认的浏览器 (比如Firefox). 17 | 18 | 在 mu4e email 界面, 按下 =g= 就会用eww打开URL, 而通过下面的配置,我可以通过按下 =G= 键来调用FIrefox打开这个URL: 19 | #+BEGIN_SRC emacs-lisp 20 | ;; open link in firefox rather than eww 21 | (defun bjm/mu4e-view-go-to-url-gui () 22 | "Wrapper for mu4e-view-go-to-url to use gui browser instead of eww" 23 | (interactive) 24 | (let ((browse-url-browser-function 'browse-url-default-browser)) 25 | (mu4e-view-go-to-url))) 26 | ;; bind it 27 | (define-key mu4e-view-mode-map (kbd "G") 'bjm/mu4e-view-go-to-url-gui) 28 | #+END_SRC 29 | 30 | 类似的,在 elfeed 中, 按下 =b= 会使用eww(Emacs默认浏览器)打开文章. 而通过下面配置,我可以通过按下 =B= 来调用Firefox访问这篇文章. 31 | 注意,我这里是通过调用Mac的 =open= 命令来实现使用系统默认浏览器访问URL的. 32 | 若你是使用linux操作系统,那么应该改成使用 =xdg-open= 命令. 33 | #+BEGIN_SRC emacs-lisp 34 | ;; browse article in gui browser instead of eww 35 | (defun bjm/elfeed-show-visit-gui () 36 | "Wrapper for elfeed-show-visit to use gui browser instead of eww" 37 | (interactive) 38 | (let ((browse-url-generic-program "/usr/bin/open")) 39 | (elfeed-show-visit t))) 40 | 41 | (define-key elfeed-show-mode-map (kbd "B") 'bjm/elfeed-show-visit-gui) 42 | #+END_SRC 43 | -------------------------------------------------------------------------------- /eww/eww对isearch的超棒支持让我大吃一惊.org: -------------------------------------------------------------------------------- 1 | #+TITLE: eww对isearch的超棒支持让我大吃一惊 2 | #+URL: https://www.reddit.com/r/emacs/comments/9oi1e3/ewws_awesome_isearch_support_just_blew_my_mind/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: eww 5 | #+DATE: [2019-04-27 六 19:11] 6 | #+LANGUAGE: zh-CN 7 | #+STARTUP: inlineimages 8 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 9 | 10 | 我所学到的: 11 | 12 | 如果某个web页面包含导航链接,且它的 'rel' 属性时 "next", "prev", "up", 或 "start" (以及其他一些支持的同义词), 那么你可以通过在 =eww= 输入 =n= ext, =p= revious, =u= p, =t= op 进行导航; 就像在 =info= 阅读器里面一样. 这还不算完. 13 | 14 | 更让人开眼的是当我在页面中使用 isearch, 在buffer中没有找到匹配项, 然后再按下 =C-s= 想从头开始搜索, 结果意外地发现跳转到了网页顺序中包含该匹配项的下一页地方了. 15 | 16 | 也就是说. 就好像是 =info= 中的 isearch 一样 -- 但是是网页版的. eww 不断地访问这些 "next" 链接直到找到另一个有匹配项的网页. (内部使用的时 misearch.el, 因此eww中的相关代码量非常少. 超酷.) 17 | 18 | 特别是对于本地托管的HTML文档来说(eww 非常适合这种场景, 而且这种情况下该行为也非常的快速), 这项特性绝对是棒极了. 19 | 20 | 若你使用 eww 访问文档, 而HTML中又不包含 'link' 导航元素, 这个理由足够要求文档维护人员添加这些导航元素. (而如果你不使用eww访问文档, 那这应该是个足够好的理由让你开始使用它) 21 | -------------------------------------------------------------------------------- /eww/超越编辑器的边界(在Emacs中用XWidget浏览网页).org: -------------------------------------------------------------------------------- 1 | #+TITLE: 超越编辑器的边界(在Emacs中用XWidget浏览网页) 2 | #+URL: http://emacsninja.com/posts/on-transcending-borders.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-09-23 五 07:42] 6 | #+OPTIONS: ^:{} 7 | 8 | 今天[[https://www.reddit.com/r/emacs/comments/4241oy/xwidget_branch_has_been_merged_into_emacs_251/][Reddit]] 上有消息说xwidget branch终于merge到emacs-25 branch中了. 既然目前还没有关于使用该功能的评测出来, 我决定自己亲自尝试一下. 9 | 构建的过程很简单: 10 | 11 | #+BEGIN_SRC sh 12 | git checkout emacs-25 13 | ./configure --with-x-toolkit=gtk3 --with-xwidgets 14 | make 15 | #+END_SRC 16 | 17 | 随后你可以在emacs中执行 =M-x xwidget-webkit-browse-url= 然后输入完整的URL. 注意是完整的URL. 如果你只输入 ~www.gnu.org~ 那只会显示一个空白的页面,需要输入完整的 ~http://www.gnu.org/~ 才行 18 | 19 | [[http://emacsninja.com/img/xwidgets-gnu.png]] 20 | 21 | 结果就像这样. 22 | 23 | [[http://html5test.com/][html5test.com]] 的评测结果也不赖: 24 | 25 | [[http://emacsninja.com/img/xwidgets-html5.png]] 26 | 27 | 甚至还能看YouTube! 28 | 29 | [[http://emacsninja.com/img/xwidgets-yt.png]] 30 | 31 | 但是出了通过点击链接导航之外,其他的操作问题就多了. 每次我试图与视频交互时,声音的音量都会被调到最大,然后整个widget开始一闪一闪的. 32 | 搜索其他的视频的时候也不自然. 你可以点击文本框,但是任何你输入的键都会被当成是Emacs命令的快捷键. 正确的做法是按下回车,然后会有一个read-string的提示符让你输入要填入文本框的内容. 33 | Except when it doesn’t: 34 | 35 | [[http://emacsninja.com/img/xwidgets-wp.png]] 36 | 37 | 目前,整合的过程才刚刚开始. 除了上面提到的问题外,你还时不时的会遇到其他一些问题: 38 | 更改window大小或者拖拉滚动条后,会看到buffer上有一段文字提醒你要再按下 =a= 键来重写调整widget的大小. 但它没有跟你说实际上你可能还需要再把滚动条拉上去才行... 39 | 40 | 我觉得这种借助webkit并在此基础上支持JavaScript的方法虽然与一般方法反其道而行但更容易得到我们想要的结果。它能用更少的Lisp代码得到更好的结果。 41 | -------------------------------------------------------------------------------- /generate_index.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | declare -A catalog_comment_dict 4 | catalog_comment_dict=([calc]="关于Calc的内容" [dired]="文件管理" [advertisement]="别看广告,看疗效" [elisp-common]="关于elisp的内容" [org-mode]="关于org-mode的内容" [eww]="Emacs看片,指日可待" [emacs-common]="其他未分类的emacs内容" [raw]="未翻译的内容,欢迎大家领取" [reddit]="reddit好问题" [Eshell]="Eshell之野望" [processing]="正在翻译的内容,别人的东西可不要抢哦~" [email]="使用Emacs收发邮件" [fun]="娱乐至上" [spellcheck]="你的错误,由我来发现") 5 | 6 | catalogs=$(for catalog in ${!catalog_comment_dict[*]};do 7 | echo $catalog 8 | done |sort) 9 | 10 | function get_contributors() 11 | { 12 | echo "* Contributors" 13 | echo "感谢GitHub以及:" 14 | # git log --pretty='%an<%ae>'|grep -viEw 'darksun|lujun9972' |sort|uniq|sed -e 's/^/+ /' 15 | git shortlog --summary --email HEAD |grep -viEw 'darksun|lujun9972'|cut -f2|sed -e 's/^/+ /' 16 | echo "" 17 | echo "感谢大家的热情参与,也欢迎更多的志愿者参与翻译,参与的方法可以参见 [[https://github.com/lujun9972/emacs-document/wiki/%E7%BF%BB%E8%AF%91%E6%8F%90%E7%A4%BA][Emacs-document Wiki]]" 18 | } 19 | 20 | function generate_headline() 21 | { 22 | local catalog="$*" 23 | echo "* $catalog" 24 | echo ${catalog_comment_dict[$catalog]} 25 | echo 26 | generate_links $catalog |sort -t "<" -k2 -r 27 | } 28 | 29 | function generate_links() 30 | { 31 | local catalog=$1 32 | if [[ ! -d $catalog ]];then 33 | mkdir -p $catalog 34 | fi 35 | posts=$(find $catalog -maxdepth 1 -type f) 36 | old_ifs=$IFS 37 | IFS=" 38 | " 39 | for post in $posts 40 | do 41 | modify_date=$(git log --date=short --pretty=format:"%cd" -n 1 $post) # 去除日期前的空格 42 | if [[ -n "$modify_date" ]];then # 没有修改日期的文件没有纳入仓库中,不予统计 43 | postname=$(basename $post) 44 | url="$(grep '^#+URL:' ${post} |sed 's/^#+URL: *//')" 45 | echo "+ [[https://github.com/lujun9972/emacs-document/blob/master/$post][$postname]] <$modify_date> [[${url}][原文地址]]" 46 | fi 47 | done|sort -k 2 48 | IFS=$old_ifs 49 | } 50 | 51 | get_contributors 52 | 53 | for catalog in $catalogs 54 | do 55 | generate_headline $catalog 56 | done 57 | -------------------------------------------------------------------------------- /org-mode/Ispell在org-mode中的正确使用方式.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Ispell在org-mode中的正确使用方式 2 | #+URL: http://endlessparentheses.com/ispell-and-org-mode.html?source=rss 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: org-mode 5 | #+DATE: [2016-06-23 周四 11:03] 6 | #+OPTIONS: ^:{} 7 | 8 | 若你曾经试过在org-mode中作拼写检查,你就知道这有多难搞了. Ispell会检查任何内容,即使是代码块和属性drawer中的内容也会检查! 若你在用org来写一篇带有代码片段的博文时,这种行为真的很烦人. 你可以用下面的方法解决这个问题. 9 | #+BEGIN_SRC emacs-lisp 10 | (defun endless/org-ispell () 11 | "Configure `ispell-skip-region-alist' for `org-mode'." 12 | (make-local-variable 'ispell-skip-region-alist) 13 | (add-to-list 'ispell-skip-region-alist '(org-property-drawer-re)) 14 | (add-to-list 'ispell-skip-region-alist '("~" "~")) 15 | (add-to-list 'ispell-skip-region-alist '("=" "=")) 16 | (add-to-list 'ispell-skip-region-alist '("^#\\+BEGIN_SRC" . "^#\\+END_SRC"))) 17 | (add-hook 'org-mode-hook #'endless/org-ispell) 18 | #+END_SRC 19 | -------------------------------------------------------------------------------- /org-mode/MacOS上捕获Emacs Org Mode代办模板的全局热键.org: -------------------------------------------------------------------------------- 1 | #+TITLE: MacOS上捕获Emacs Org Mode代办模板的全局热键 2 | #+URL: https://christiantietze.de/posts/2018/05/emacs-org-mode-global-capture-macos/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: org-mode 5 | #+DATE: [2020年 02月 14日 星期五 12:30:24 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | [[https://vg01.met.vgwort.de/na/38ac0231c58b4b7889745a9ac61697d7]] 10 | 11 | 我使用GNU Emacs来做各种事情有一段时间了。我正在考虑将我的任务管理器从OmniFocus迁移到Org模式。 12 | 目前,我最钟意的就是Org文件的自由性。我可以有一个笔记大纲,其中充斥着 =代办(TODO)= 项目。 13 | 这样我就可以把正在解决的难题记下来,把以后要做的事情标记出来。 14 | 它能提醒我在提交代码之前进行清理(例如编写或修改测试,删除调试代码),非常有用。我喜欢它。我已经习惯了走捷径,所以日常生活中的大部分痛苦都消失了。 15 | 16 | 今天我玩了一下 =org-capture=. 这个命令会打开一个临时文本(即一个缓冲区)输入字段,并将该条目存储在特定的位置。 17 | 你需要配置[[https://orgmode.org/manual/captemplates.html][Capture templates]]来让它工作,它们可以做一些非常神奇的事情,比如“将捕获项插入文件foo.txt中,标题XYZ下的当前日期下”。啊,纯文本的力量。 18 | 19 | 一个非常简单的捕获模板例子就是将你inbox中的 =TODO= 项目存到 =~/org/inbox.org= 中: 20 | 21 | #+begin_src emacs-lisp 22 | (setq org-capture-templates 23 | (quote (("t" "todo" entry (file "~/org/inbox.org") 24 | "* TODO %?n%an")))) 25 | #+end_src 26 | 27 | 当调用 =org-capture= 并输入“t”选择此模板时,它将使用以 =*TODO= 开头的新列表项为你作为临时的文本条目。 28 | 在光标位置(由模板中的 =%?= 指定)的下一行,插入了当前位置的链接(=%a=)。 29 | 该链接可以是一个文件,也可以是Org文档中的一个位置。你可以单击这些链接跳转到当前位置。 30 | 可以这么说,这些 =TODO= 项对系统范围内的文件进行了注释。 31 | 32 | 现在,如果你暂时没在使用Emacs,却想从Xcode或Chrome或其他什么地方为inbox捕获一个任务,就可以使用 =emacsclient= 命令行应用程序远程控制Emacs。 33 | 它将能够让你在当前Emacs会话中执行Lisp命令。 34 | 35 | 首先,使用命令 =server-start= 启动Emacs服务器。(您可以使用M-x执行命令,M-x是Meta-x的缩写。必须首先配置好Meta键。例如,我把它配置成左command键。) 36 | 37 | 我在上面粘贴的模板有一个键“t”,所以使用 =org-capture= 的远程控制命令是这样的: 38 | 39 | #+begin_src shell 40 | $ /usr/local/bin/emacsclient org-protocol://capture?template=t 41 | #+end_src 42 | 43 | 要启用 =org-protocol://= 作为识别参数,你还需要在Emacs中启用一个可选模块。该模块名为 =org-protocol=,而保存启用模块列表的变量被是 =org-modules=: 44 | 45 | #+begin_src emacs-lisp 46 | (setq org-modules (quote (org-protocol))) 47 | #+end_src 48 | 49 | 实际上,我是使用Emacs的“customize”工具进行配置的,该工具存储用户首选项(在macOS应用程序中,可以按下Cmd-进入其中)。 50 | 搜索“Modules”,展开“Org Modules”,选择“protocol”模块。然后应用和保存更改。 51 | 52 | [[https://christiantietze.de/posts/2018/05/emacs-org-mode-global-capture-macos/emacs-customize-org-modules.png]] 53 | 54 | Org模块展开后的交互式自定义项编辑器 55 | 56 | 当服务器运行并且启用了 =org-protocol= 后,shell命令就会像你自己调用捕获模板(在Emacs中输入 C-c c t)一样工作 57 | 58 | 我将它绑定到一个Keyboard Maestro 宏上,它代替了我在OmniFocus中输入新任务的全局快捷键。如果Emacs应用程序还不是最前面的应用程序,那么宏也会把它放到前面。 59 | -------------------------------------------------------------------------------- /org-mode/Org-mode与Hyperbole之间的区别.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Org-mode与Hyperbole之间的区别 2 | #+AUTHOR: lujun9972 3 | #+URL: https://lists.gnu.org/archive/html/emacs-devel/2016-06/msg00246.html 4 | #+CATEGORY: org-mode 5 | #+DATE: [2016-08-08 周一 14:36] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | 虽然从表面上来看这两者很接近,但是一旦你开始使用这两者,你很快就能发现其中的不同点. 现在它们是完全不一样的,也许以后会有一些交集吧. 10 | 11 | Org-mode是一个基于结构化文件的major-mode,它是建立在Emacs的outlinning mode之上的. 12 | Hyperbole是一个整合Emacs各项功能的一个系统,它提供了随处可及的快捷键以及hyperbuttons. 13 | 14 | Org-mode的大纲就是Emacs的普通的大纲. 15 | Hyperbole的Koutliner是有唯一性的; 每个节点/段落都有唯一的一个id以及相应的属性. 当你修改大纲时,节点/段落的id也会自动更像,你可以很清楚的知道id为2b4的节点就是id为2b的节点的子节点,同时也是id为2b3节点的兄弟节点. 16 | 17 | Org-mode需要你明确的创建一个超链接. 18 | Hyperbole这有这项功能,只需要在不同的window之间用鼠标一拖,就能在任意类型的文本文件中创建超链接. 19 | 但是Hyperbole同时还能识别内嵌于多种类型的文件或buffer中的超链接. 而且它也很容易扩展支持新的超链接类型. 你只需要按下一个按钮,Hyperbole就会在相关的上下文环境中作该做的事情. 20 | 21 | Org-mode可以管理待办,计时以及一些基础的项目管理功能. 22 | Hyperbole并没有相关的功能,但是它可以与你喜欢的待办管理功能相整合. 23 | 24 | Org-mode据我所做是没有联系人管理功能的(不是很确定因为我没有用过相关功能). 25 | Hyperbole有一个快速而有效率的分级联系人管理器. 26 | 27 | Org-mode只是个major-mode而已,因此它不会去修改你的buffers, windows 以及frames. 28 | Hyperbole有一个快速的,经过仔细设计的window及frame管理器. 这个管理器可以让你快速调整你的Emacs成你想要的样子. 29 | 最终,这些window及frame配置信息可以被保存起来并且可以有链接指向这些配置信息,这样你就能够为不同任务分配一个样式,然后按下一个按钮就立即切换到成那个样式(类似于整合了Hyperbole其他功能的Workspace). 30 | 31 | Org-mode无法管理库(library)的信息. 32 | Hyperbole能够整理,连接,搜索以及访问文本文件中的库. 33 | 34 | Hyperbole还有加速代码浏览以及修改结构化的代码的功能. 并且还支持像Control和Meta那样使用鼠标,这样你就能很方便的操作屏幕上的东西了. 35 | 36 | 不知道这些有没有回答到你的问题? 37 | 38 | 如果你想了解的更多,那就好好坐下来阅读一下manual吧. 会很有帮助的. 39 | 40 | Bob 41 | -------------------------------------------------------------------------------- /org-mode/git: 用post-commit hook来探测Org-mode中的大量被删除的行.org: -------------------------------------------------------------------------------- 1 | #+TITLE: git: 用post-commit hook来探测Org-mode中的大量被删除的行 2 | #+URL: https://karl-voit.at/2014/08/20/org-losses-determining-post-commit/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: org-mode 5 | #+DATE: [2018年 10月 25日 星期四 12:00:03 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 可能你也遇到过相同的问题: 当大型 [[http://orgmode.org][Org-mode]] 层级架构被折叠时, 你可能会无意中覆盖或删除 Org-mode 文件中的大量内容. 10 | 11 | 若你切好用 [[http://git-scm.com/][git]] 仓库来管理 Org-mode 文件,那这没有什么大不了的. 我甚至使用 [[https://github.com/nevik/gitwatch][gitwatch]] 来自动提交 Org-mode 文件的所有更改. 12 | 任何Org-mode文件中的更改都可以被回撤. 只不过, 你需要 *认出* 被删掉的这些行以便能够恢复之. 13 | 14 | 下面这个技巧可以帮到你: 使用下面这个 [[http://git-scm.com/book/en/Customizing-Git-Git-Hooks][post-commit hook]] , 当你签入一个超过一定行数的提交时就会在日常agenda中产生一个警告. 15 | 16 | 你需要修改下面的代码才能使之工作. 请将内容中的 =OUTFILE= 修改为你 =org-agenda-files= 中的某个文件. 任何该脚本产生的警告信息会添加到该文件中. 17 | 警告信息报告当前(提交)的时间戳, 你不可能会遗漏这条信息: 它放在你的日常agenda中. 18 | 19 | 你也可能需要修改 =THRESHOLD= 的值. 该值的意思是单个文件在不产生任何警告的情况下最多能删除多少行. 20 | 21 | #+begin_src shell 22 | #!/bin/sh 23 | # 24 | # An example hook script that is called after a successful 25 | # commit is made. 26 | # 27 | # To enable this hook, rename this file to "post-commit". 28 | 29 | ## current/last commit: 30 | CURRENT_HASH=`git rev-parse HEAD` 31 | 32 | ## the commit before: 33 | PREVIOUS_HASH=`git rev-parse HEAD^1` 34 | 35 | ## the file where the message(s) are being written to: 36 | OUTFILE="${HOME}/org/errors_orgmode_commits.org" 37 | 38 | ## number of lines that are OK to be deleted in one single file: 39 | THRESHOLD=250 40 | 41 | 42 | MESSAGE="** commit ${CURRENT_HASH} deleted more than ${THRESHOLD} lines in a file!" 43 | 44 | DETAILS="#+BEGIN_SRC sh :results output 45 | cd ${HOME}/org 46 | echo \"commit ${CURRENT_HASH}\" 47 | git diff --stat \"${PREVIOUS_HASH}\" \"${CURRENT_HASH}\" 48 | ,#+END_SRC" 49 | 50 | 51 | ## manual test with: 52 | ## git diff --stat `git rev-parse HEAD^1` `git rev-parse HEAD` 53 | 54 | git diff --numstat "${PREVIOUS_HASH}" "${CURRENT_HASH}" | \ 55 | cut -f 2 | \ 56 | while read line 57 | do test "$line" -gt "${THRESHOLD}" && \ 58 | echo "${MESSAGE}\n<`date '+%Y-%m-%d %H:%M'` +1d>\n\n${DETAILS}\n" >> \ 59 | "${OUTFILE}"; \ 60 | done 61 | 62 | #end 63 | #+end_src 64 | 65 | 这样当你在agenda中发现一个警告时, 点击它,甚至可以在标题下的babel代码片段上按下 =C-c C-c= 进行执行. 它会告诉你提交的详细信息. 66 | 67 | 解决问题之后,只需手动删除error-Org-mode文件中已解决错误的标题就行了。 68 | -------------------------------------------------------------------------------- /org-mode/org-mode中一次性为多个headline添加tag.org: -------------------------------------------------------------------------------- 1 | #+TITLE: org-mode中一次性为多个headline添加tag 2 | #+AUTHOR: lujun9972 3 | #+TAGS: org-mode 4 | #+DATE: [2017-02-28 二 14:18] 5 | #+LANGUAGE: zh-CN 6 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 7 | 8 | 要在org-mode文件中为多个headline一次性添加/删除tag,只需要 9 | 1. 选中要添加/删除tag的那几个headline 10 | 2. 运行 =M-x org-change-tag-in-region=. 11 | 3. 输入要添加/删除的tag 12 | 4. 告诉Emacs你是想添加还是删除tag 13 | -------------------------------------------------------------------------------- /org-mode/一个博士生是怎么应用Org-mode的.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 一个博士生是怎么应用Org-mode的 2 | #+URL: http://matthieu.io/blog/2016/10/22/org-mode-phd/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: org-mode 5 | #+DATE: [2016-10-29 六 20:49] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 10 | 我差不多是在开始读博士的时候接触到的 [[http://orgmode.org/][org-mode]] , 我发现用它真是一件很棒的工具,我可以用它我追踪我做过的每一件事情. 下面我就来说说我是怎么用orgmode来管理自己的工作的,让我们一个文件一个文件地说: 11 | 12 | * research.org 13 | 14 | 我用这个文件来追踪那些在读的论文, 每篇论文都有独立的代办事情,截止日期等内容. 我也会把一些偶尔想到的idea记在这里以防忘掉了. 15 | 16 | 好处: 可以将实验步骤记录在orgmode的内嵌代码块中,这样可以重现研究过程了. 17 | 18 | * teaching.org 19 | 20 | 这里存放的关于教学方面的内容: 里面按年份和学习组织了我要教的课程,相关的笔记,上课的学生,以及电子邮件中的相关信息. 21 | 22 | 好处: 可以用org模拟幻灯片 23 | 24 | * readings.org 25 | 26 | 这个文件存的东西很简单: 它里面存放的所有我读过的论文,相关的笔记以及读完的日期. 这在我写论文时非常有用. 27 | 28 | 好处: 可以建立链接连接到本地的PDF文件,可以快速的访问这些文件. 29 | 30 | * learning.org 31 | 32 | 我参加过的所有课程及其我记录的相关笔记. 同时也是提醒我不要忘了上课. 这样一来我可以很方便的追踪我的ECTS (European class credits) 并且能保证我的论文最终不会缺少东西(I can know if I still miss some to be allowed to defend my thesis at the end). 33 | 34 | * meetings.org 35 | 36 | 我参加过的所有会议以及相关记录. 37 | 38 | 好处: 可以设置循环任务来提醒我与博士导师见面. 39 | 40 | * backlog.org 41 | 42 | 里面存的是那些杂项事物 (一般是那些管理性的任务). 设置deadline的功能相当有用! 43 | 44 | * travels.org 45 | 46 | 记录了旅行时需要保存的那信息。比如我要去参加会议时的相关内容. 另外也能提醒我什么时候到过哪些地方. 47 | 48 | * All in all 49 | 50 | 还有一些其他的org文件, 不过上面列出的那些org文件都是与我的博士生涯有关的. 我还有一个 =projects.org= 用来记录那些我可能永远不会取实现的点子的(不过,过几年之后再回过头来看还觉得蛮有趣的!), 还有一个 =writings.org= (本文的草稿就是写在这份文件里的), 以及其他另外的org文件. 51 | 我在我的待办事项中打量地应用了时间戳来记录截止日期等其他一些重要的日期. 52 | 53 | 如果你有其他更好的想法, 请告诉我,我很乐意接受! 54 | -------------------------------------------------------------------------------- /org-mode/为Org表格中的域和列设置公式的简单方法.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 为Org表格中的域和列设置公式的简单方法 2 | #+URL: http://mbork.pl/2018-02-25_Simple_way_of_assigning_formulas_to_fields_and_columns_in_Org_tables 3 | #+AUTHOR: lujun9972 4 | #+TAGS: org-mode 5 | #+DATE: [2019-04-27 六 18:37] 6 | #+LANGUAGE: zh-CN 7 | #+STARTUP: inlineimages 8 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 9 | 10 | 今天我要分享一个关于Org-mode表格的小窍门. 11 | 我知道在Org-mode表格单元格中按下 ~C-u C-c =~ 可以为单元格设置一个公式 (或者按下 ~C-c =~ 来设置一个列公式). 12 | 然而, [[https://orgmode.org/manual/Field-and-range-formulas.html][还有更简单的方法]]: 只需要输入 ~:=~ 然后跟上公式再按下 ~TAB~, ~RET~ 或者 ~C-c C-c~ 就行了. ([[https://orgmode.org/manual/Column-formulas.html][没有冒号的话,则表示列公式]].) 非常方便! (这个技巧一直都收录在手册中... 我又一次意识到时常浏览手册真是个好主意!) 13 | 14 | -------------------------------------------------------------------------------- /org-mode/使用Org-mode代替delicious(书签管理).org: -------------------------------------------------------------------------------- 1 | #+TITLE: 使用Org-mode代替delicious(书签管理) 2 | #+URL: https://lists.gnu.org/archive/html/emacs-orgmode/2012-06/msg00640.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: org-mode 5 | #+DATE: [2016-06-18 六 23:21] 6 | #+OPTIONS: ^:{} 7 | 8 | 作为 =delicious.com= 的长期用户, 我曾经很喜欢Yahoo!转给«sunset»之前的那个旧delicious:当时它能在存储书签时为书签标记tag(并且具有一堆tag补全机制),有很好的浏览器插件, 有能用的Android客户端, 提供了tag管理功能.(重命名,合并,删除tag), 具有很好的搜索功能, 可以为整个tag组或某几个tag下的书签发布RSS feeds. 9 | 10 | 借助于 Memacs[fn:4] (RSS module),我能使用Org-Mode实现类似的书签管理功能. 11 | 12 | 从去年开始,delicious提供了一堆我根本不需要也不想要的功能. 更糟糕的是,它还暂停/改变了我常用的很多功能. 同时性能也有所下降. 13 | 14 | 在我转到它的直接竞争对手(例如http://diigo.com) 那之前,我在想Org-Mode是否能提供一个可用的解决方案. 目前看来,该解决方案有一个明显的劣势那就是无法发布RSS feeds(at least out-of-the-box).但同时,该方案也有它的优势: 完全的本机服务,不依赖于某个网络公司的商务策略,与我的Org-mode完全整合, one (external) technology less to worry about. 15 | 16 | -> 真的有人觉得用Org-mode来进行书签管理能够与delicious相媲美吗? 17 | 18 | 不可否认,确实有一大帮人只是用bookmarks.org或其他类似网站来存储URL而已. 但是我想要的刻不仅仅是这样而已哦:-) 19 | 20 | 我的需求和解决方案如下: 21 | 22 | 23 | * 存储浏览器种的书签(我用chrome) 24 | 25 | org-protocol.el[fn:1] 看起来蛮有前途的,也[fn:2]蛮有趣的. 可惜它尚不能很方便的添加标签: 我需要先将URL发送到Emacs Org-mode,然后切换到Emacs,跳转到最近添加的那条记录,进入tagging mode,然后才能添加标签(Org-mode具有标签补全功能), 最后还要退出 tagging mode. 这意味着目前的解决方案要多出很多额外的步骤. 26 | 27 | 我很不喜欢这些额外的步骤. 28 | 29 | 30 | * 在Android上存储书签 31 | 32 | Android版的Mobile-org[fn:3] 很牛逼,但是它的分享/捕获机制也没有很好的支持打标签的功能. 尤其崩溃的居然是缺少标签补全功能. 33 | 34 | 目前看来,似乎没有什么很好的替代品可以替代delicious Android应用. 35 | 36 | 37 | * 标签管理(重命名,合并与删除) 38 | 39 | 我得承认我添加了很多的标签,有时也要使用给予标签的搜索与查询. 但是就目前看来,管理tag并不是什么问题,因为Emacs原生就支持正则查找与替换等功能. 40 | 41 | 该功能没什么问题. 42 | 43 | 44 | * 搜索书签 45 | 46 | 普通的搜索,给予sparse tree的搜索,正则搜索...,Emacs/Org-mode在这方面要好太多了. 47 | 48 | 49 | * 发布RSS; Social Web Methods 50 | 51 | 也许以后能够基于Org-mode导出RSS feed, 也可以编写一个外部脚步来解析Org-mode种的CREATED属性并抽取出最近的20个记录来生成feed. 使用Python来写的话,应该不会太难. 52 | 53 | 虽然我真的很喜欢delicious的这个功能,但是我想没有该功能应该也OK. 54 | 55 | 56 | * 其他 57 | 58 | 还有其他书签管理方面有趣的功能我没有提到吗? 59 | 60 | * Footnotes 61 | 62 | 63 | [fn:1] http://orgmode.org/worg/org-contrib/org-protocol.html 64 | 65 | [fn:2] http://orgmode.org/worg/org-contrib/org-annotation-helper.html 66 | 67 | [fn:3] https://github.com/matburt/mobileorg-android/wiki/ 68 | 69 | [fn:4] https://github.com/novoid/Memacs 70 | 71 | -------------------------------------------------------------------------------- /org-mode/口袋中的org-mode.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 口袋中的org-mode 2 | #+URL: https://nakkaya.com/2010/03/19/org-mode-in-your-pocket-setting-up-mobileorg/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: org-mode 5 | #+DATE: [2018年 09月 12日 星期三 19:02:19 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | This post documents the steps required to configure org-mode so it can sync with MobileOrg. 10 | 11 | [[http://mobileorg.ncogni.to][MobileOrg]] 是一个iPhone应用程序,它可以让你随时查看,修改org文件的行动。 12 | 这是一个很好的应用程序,但是文档很少,而且有点混乱。这篇文章记录了配置org-mode以便让它就可以与MobileOrg同步所需的步骤。 13 | 14 | org-mode默认查看 =~/org/= 文件夹中的org文件,如果你把它们放在其他地方,设置 =org-directory= 变了指向它, 15 | 16 | #+begin_src emacs-lisp 17 | (setq org-directory "~/Documents/org/") 18 | (setq org-mobile-inbox-for-pull "~/Documents/org/from-mobile.org") 19 | #+end_src 20 | 21 | MobileOrg使用WebDav来同步文件,如果你把WebDav挂载成一个磁盘,你需要设置 =org-mobile-directory= 指向它,你也可以使用org-mobile push/pull钩子和scp代替。 22 | 23 | #+begin_src emacs-lisp 24 | (setq org-mobile-directory "/Volumes/nakkaya.com/org/") 25 | #+end_src 26 | 27 | 在默认情况下,没有文件会被暂存到WebDav中,你需要将 =org-mobile-files= 设置为你想在iPhone上访问的文件列表, 28 | 29 | #+begin_src emacs-lisp 30 | (setq org-mobile-files (quote ("gtd.org"))) 31 | #+end_src 32 | 33 | 当你同步org文件时,org-mobile会给你的文件添加一个属性抽屉(property drawer),如果你不想要它,你可以这么设置, 34 | 35 | #+begin_src emacs-lisp 36 | (setq org-mobile-force-id-on-agenda-items nil) 37 | #+end_src 38 | 39 | 但是要注意,如果你有像这样的文件结构, 40 | 41 | #+begin_src org 42 | ,* Task 43 | ,** SubTask 44 | ,* Task 45 | ,** SubTask 46 | #+end_src 47 | #+BEGIN_EXAMPLE 48 | #+END_EXAMPLE 49 | 50 | 如果你编辑其中一个 =SubTask= ,org-mobile将无法确定编辑的是哪一个子任务,除此之外没有其他问题了。 51 | 至于agenda,只有你自定义的agenda视图会被同步,我还建议将 =org-agenda-show-all-date= 设置为nil,以便过滤没有内容的日期,使查看agenda更方便。 52 | 53 | #+begin_src emacs-lisp 54 | (setq org-agenda-custom-commands 55 | '(("w" todo "TODO") 56 | ("h" agenda "" ((org-agenda-show-all-dates nil))) 57 | ("W" agenda "" ((org-agenda-ndays 21) 58 | (org-agenda-show-all-dates nil))) 59 | ("A" agenda "" 60 | ((org-agenda-ndays 1) 61 | (org-agenda-overriding-header "Today"))))) 62 | #+end_src 63 | -------------------------------------------------------------------------------- /org-mode/在Org-Mode-table中使用自定义elisp函数进行计算.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在Org-Mode-table中使用自定义elisp函数进行计算 2 | #+AUTHOR: lujun9972 3 | #+CATEGORY: org-mode 4 | #+DATE: [2016-06-17 周五 10:14] 5 | #+OPTIONS: ^:{} 6 | 7 | UPDATE 2015-06-17: 在下面的评论中, Will指出若在表格中使用正确的日期格式([yyyy-mm-dd]而不是yyyy-mm-dd), Org会自动为你计算时间差. Neato! 下面是Will的原话: 8 | #+BEGIN_EXAMPLE 9 | Hi Sacha. 你知道吗,Org允许你直接针对(inactive或active)timestamp作运算? 当两个时间戳之间的time部分不一致时,它甚至能给你一个精确的小数天数.: 10 | 11 | | Start | End | Interval | 12 | |------------------------+------------------------+----------| 13 | | [2015-06-16 Tue] | [2015-06-23 Tue] | 7 | 14 | | <2015-06-13 Sat> | <2015-06-15 Mon> | 2 | 15 | | [2015-06-10 Wed 20:00] | [2015-06-17 Wed 08:00] | 6.5 | 16 | ,#+TBLFM: $3=$2 - $1 17 | #+END_EXAMPLE 18 | 19 | 下面是我之前的复杂做法… =) 20 | 21 | ---- 22 | 23 | 最近我写了一片使用Org Mode table[[http://sachachua.com/blog/2015/06/using-emacs-org-mode-tables-to-calculate-doses-to-buy/][计算购买总量y]] 的文章. 经过再三开率, 还是觉得直接在org mode table中使用Emacs Lisp 函数来计算会比写个函数来处理并输出整个表格要更容易也更灵活一些. 24 | 25 | 首先,我们需要定义一个函数用来计算两个日期之间(包括这两个日期)的间隔天数. 代码如下: 26 | #+BEGIN_SRC emacs-lisp 27 | (defun my/org-days-between (start end) 28 | "Number of days between START and END. 29 | This includes START and END." 30 | (1+ (- (calendar-absolute-from-gregorian (org-date-to-gregorian end)) 31 | (calendar-absolute-from-gregorian (org-date-to-gregorian start))))) 32 | #+END_SRC 33 | 34 | 下面改进后的表格. 我将“Needed”列放到医药种类得左边, 这样方便我阅读和确认. 35 | #+BEGIN_SRC org 36 | | Needed | Type | Per day | Start | End | Stock | 37 | |--------+--------------+---------+------------+------------+-------| 38 | | 30 | Medication A | 2 | 2015-06-16 | 2015-06-30 | 0 | 39 | | 2 | Medication B | 0.1 | 2015-06-16 | 2015-06-30 | 0.2 | 40 | ,#+TBLFM: @2$1..@>$1='(ceiling (- (* (my/org-days-between $4 $5) (string-to-number $3)) (string-to-number $6))) 41 | #+END_SRC 42 | 43 | 在#+TBLFM:这一行按下 =C-c C-c= 会自动更新第一列的值. 44 | 45 | =@2$1..@>$1= 表示第一列($1)的第二行(@2)到最后一行(@>)的单元. ='= 告诉Org将紧跟着的表达式看成Emacs-Lisp来计算.计算时会替换指定的值(例如$4被替换为第四列的值). 46 | 47 | 表格中用来计算第一列(Needed)的公式需要知道你每天的消耗,从那天开始到那天结束(包括这两天)以及先有的库存. 计算出来的结果通过 =ceiling= 函数向上取整. 48 | 49 | 由于该等式从会从表格中的每行中取相应数据, 因此所有行中的开始与结束日期都必须有值(即使这些值其实每行都一样的). 为了快速复制这些值到下一行,设置 =org-table-copy-increment= 为nil, 然后在你希望复制上一行值的单元上按下 =S-return= (shift-return)即可. 不断按 =S-return= 就会不断的往下复制. 50 | 51 | 由于表格计算公式中各表格的内容会当成是字符串来看待, 因此有些值需要使用 =string-to-number= 来将之转换为数字才能进行乘法和减法运算. 若所有表格内容都是数字,则可以通过 =;N= 标志让org自动帮你作转换. 像下面这样: 52 | #+BEGIN_SRC org 53 | | Needed | Type | Per day | Days | Stock | 54 | |--------+--------------+---------+------+-------| 55 | | 6 | Medication A | 2 | 3 | 0 | 56 | | 1 | Medication B | 0.1 | 3 | 0.2 | 57 | ,#+TBLFM: @2$1..@>$1='(ceiling (- (* $3 $4) $5)));N 58 | #+END_SRC 59 | -------------------------------------------------------------------------------- /org-mode/在org-mode中用链接的形式嵌入Youtube视频.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在org-mode中用链接的形式嵌入Youtube视频 2 | #+URL: http://endlessparentheses.com/embedding-youtube-videos-with-org-mode-links.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: org-mode 5 | #+DATE: [2016-10-16 日 08:03] 6 | #+OPTIONS: ^:{} 7 | 8 | 如果经常阅读本博客的话,你应该有注意到 [[http://endlessparentheses.com/debugger-improvements-in-cider-0-10-0.html][我在本文中插入了一篇youtube视频]]. 要插入Youtube视频其实很简单,只需要在HTML中插入它提供给你的iframe HTML代码就行了. 9 | 不过如果我们还需要手工操作的话,那就太不像是Emacs风格了. 只需要插入下面代码到你的初始化文件中,你就解脱了. 10 | 11 | #+BEGIN_SRC emacs-lisp 12 | (defvar yt-iframe-format 13 | ;; You may want to change your width and height. 14 | (concat "")) 19 | 20 | (org-add-link-type 21 | "yt" 22 | (lambda (handle) 23 | (browse-url 24 | (concat "https://www.youtube.com/embed/" 25 | handle))) 26 | (lambda (path desc backend) 27 | (cl-case backend 28 | (html (format yt-iframe-format 29 | path (or desc ""))) 30 | (latex (format "\href{%s}{%s}" 31 | path (or desc "video")))))) 32 | #+END_SRC 33 | 34 | 使用方法如下所示(可以添加描述信息). 35 | 36 | #+BEGIN_SRC org 37 | [[yt:A3JAlWM8qRM]] 38 | #+END_SRC 39 | 40 | 当你导出HTML时,org-mode会自动为你生成Youtube的嵌入代码. 相比于直接嵌入Youtube的iframe代码,这种方式你不仅可以直接在org-mode中点击链接,还能把该链接导出成其他格式. 41 | -------------------------------------------------------------------------------- /org-mode/在其他地方应用org-mode的table和structure.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 在其他地方应用org-mode的table和structure 2 | #+URL: http://pragmaticemacs.com/emacs/use-org-mode-tables-and-structures-in-emails-and-elsewhere/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: org-mode 5 | #+DATE: [2016-12-07 三 22:09] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 10 | 我好喜欢org-mode的 [[http://pragmaticemacs.com/emacs/org-mode-basics-structuring-your-notes/][lists]] 和 [[http://pragmaticemacs.com/emacs/org-mode-basics-ii-use-simple-tables-in-your-notes/][tables]] 功能. 借助于 =orgstruct-mode= 与 =orgtbl-mode= (这两个mode也是org-mode的一部分), 你也可以在其他mode下使用这些功能. 11 | 12 | 在任何major mode都能通过运行 =M-x orgstruct++-mode= 与 =M-x orgtbl-mode= 来启用这两个mode,之后你就可以使用org-mode的那些命令来创建list和table了. 13 | 我觉得在写电子邮件时,这两个功能特别有用,所以我在 [[http://pragmaticemacs.com/emacs/editing-your-emacs-config-file/][emacs配置文件中]] 添加了以下代码来为 message-mode 自定开启这两个minor mode: 14 | 15 | #+BEGIN_SRC emacs-lisp 16 | ;; use org structures and tables in message mode 17 | (add-hook 'message-mode-hook 'turn-on-orgtbl) 18 | (add-hook 'message-mode-hook 'turn-on-orgstruct++) 19 | #+END_SRC 20 | -------------------------------------------------------------------------------- /org-mode/如何自定义org-mode链接(你可以通过org-mode链接做任何事情).org: -------------------------------------------------------------------------------- 1 | #+TITLE: 如何自定义org-mode链接(你可以通过org-mode链接做任何事情) 2 | #+URL: http://endlessparentheses.com/use-org-mode-links-for-absolutely-anything.html 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: org-mode 5 | #+DATE: [2016-10-14 五 07:37] 6 | #+OPTIONS: ^:{} 7 | 8 | [[http://orgmode.org/][org-mode]] 有一个鲜为人知的功能,那就是你可以用[[http://doc.endlessparentheses.com/Fun/org-add-link-type][org-add-link-type]] 来自定义新的链接类型. 你可以用它来做很多事, 比如, 可以创建一种链接,点击这种链接就会在整个代码库中搜索指定的正则表达式. 9 | 10 | #+BEGIN_SRC emacs-lisp 11 | (org-add-link-type 12 | "grep" 'endless/follow-grep-link) 13 | 14 | (defun endless/follow-grep-link (regexp) 15 | "Run `rgrep' with REGEXP as argument." 16 | (grep-compute-defaults) 17 | (rgrep regexp "*" (expand-file-name "./"))) 18 | #+END_SRC 19 | 20 | 这样, 当你在org-mode下点击类似下面的链接时,你会看到一个搜索结果的列表. 21 | 22 | #+BEGIN_SRC org 23 | ,** TODO Refactor [[grep:OldClassName][OldClassName]] into NewClassName 24 | #+END_SRC 25 | 26 | * LINK header arguments 27 | 28 | 正如 [[http://www.reddit.com/r/emacs/comments/2d6hjs/use_orgmode_links_for_absolutely_anything/cjmyxfm][/u/blue1_ 中所指出的]], 若你新创建的链接仅仅是对URL做一些替换操作,那你还可以用 =#+LINK headers= 来实现. 29 | 30 | #+BEGIN_SRC org 31 | ,#+LINK: isbn http://www.amazon.com/dp/%s 32 | #+END_SRC 33 | 34 | * Tag Searches 35 | 36 | 让我们来看看另一个使用场景, 下面代码定义了一种链接,它会搜索你的标题来查找特定的tags. 37 | 38 | #+BEGIN_SRC org 39 | (org-add-link-type 40 | "tag" 'endless/follow-tag-link) 41 | 42 | (defun endless/follow-tag-link (tag) 43 | "Display a list of TODO headlines with tag TAG. 44 | With prefix argument, also display headlines without a TODO keyword." 45 | (org-tags-view (null current-prefix-arg) tag)) 46 | #+END_SRC 47 | 48 | 然后你可以这一条这样的链接了 49 | 50 | #+BEGIN_SRC org 51 | [[tag:work+phonenumber-boss][Optional Description]] 52 | #+END_SRC 53 | 54 | 其中搜索的语法可以参见 [[http://orgmode.org/manual/Matching-tags-and-properties.html#Match-syntax][这里]], 与[[http://doc.endlessparentheses.com/Fun/org-tags-view][ org-tags-view]] 命令中的用法一样. 55 | -------------------------------------------------------------------------------- /org-mode/对org-mode中的表格进行排序.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 对org-mode中的表格进行排序 2 | #+URL: http://pragmaticemacs.com/emacs/sorting-an-org-mode-table/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: org-mode 5 | #+DATE: [2016-12-24 Sat 21:37] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 10 | 在org-mode中,你可以快速地对表格进行排序,方法是将光标定位到表格上,然后按下 =C-c ^=. 再选择排序的类型(例如,a表示按字母顺序排序,n表示按数字来排序). 11 | 若你选择排序类型时使用了大写的字母,那么表示进行反向排序. 12 | 13 | 排序时的key取之于光标所在的列,同时文档中也说明了排序的范围: 14 | 15 | #+BEGIN_EXAMPLE 16 | The range of lines is the range between the nearest horizontal separator 17 | lines, or the entire table of no such lines exist. 18 | #+END_EXAMPLE 19 | -------------------------------------------------------------------------------- /org-mode/将org文件转换为带引用的docx文件.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 将org文件转换为带引用的docx文件 2 | #+URL: https://www.clarkdonley.com/post/convert-org-to-docx-with-citations/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: org-mode 5 | #+DATE: [2020年 02月 19日 星期三 09:50:36 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 去年这个时候,我正在写一篇论文,有人要求我把论文以 =.docx= 格式交给的教授,以便进行注释。 10 | 我当时并没有真正地把这个过程构建到我的工作流中(我总是先写 =.org= 或 =.tex= 然后转换成 =.pdf=),为此,我决定弄清楚如何实现这一转换。 11 | 现在导出成 =.docx= 对于Org模式来说并不是什么大问题,因为你可以导出成LibreOffice的 =.odt= 格式化并通过LibreOffice转换成 =.docx=。 12 | 13 | Org =.odt= 导出的问题在于它没有处理我的文献引用。 14 | 它们只会以引用命令或类似的形式出现。我找到的解决方案是使用 =pandoc= 进行转换而且效果很好。 15 | 但是它的设置选项非常繁琐,因此我在bash配置文件中创建了一个函数,这样我就可以使用 =org2docx= 命令轻松地完成这项工作。 16 | (注意:您也可以将其用于其他类型的文件(如markdown)。我这里只关心org文件) 17 | 18 | 以下是我使用的步骤: 19 | 20 | - 首先,您需要安装[[http://johnmacfarlane.net/pandoc/][pandoc]]。用 [[http://brew.sh/][homebrew]] 安装很容易: =brew install pandoc=. 21 | - 你还需要安装[[https://github.com/jgm/pandoc-citeproc][pandoc-citeproc]]: =brew install pandoc-citeproc= 22 | - 你需要 [[https://github.com/citation-style-language/styles][从github上下载.csl文件]] 并将它们放在一个易于访问的目录中。 23 | - 我认为我 =.docx= 文件创建一个客户样式模板 =reference.docx= 是一个好主意。[[https://github.com/jgm/pandoc-templates/issues/20][此处有一篇相关的短文]]。然后,你需要按照自己的喜好处理文件中的样式,并将其放在一个容易引用的位置。 24 | - 之后,用你喜欢的文本编辑器打开 =~/.bash_profile=,并添加以下函数(可能要修改有关选项,关于参数的讨论请参见下面): 25 | 26 | 27 | #+begin_src shell 28 | org2docx() { 29 | pandoc --bibliography=/path/to/your/bibliographyfile.bib --csl=/path/to/your/csl/chicago-fullnote-bibliography.csl --reference-docx=/path/to/your/reference.docx -i $1 -o $1-pandoc.docx 30 | } 31 | #+end_src 32 | 33 | - 你需要运行 =source ~/.bash_profile= 来加载更改。 34 | 35 | 现在,您只需要运行: =org2docx MyPaper.org= 就能转换文档了,相当容易,不必再为那些选项费心了。 36 | 37 | 以下是各种选项的作用: 38 | 39 | - =--bibliography=/path/to/your/bibliographyfile.bib= 指定文献文件的路径。因为我为所有的参考文献维护同一个 =.bib= 文件,因此不同的论文无需修改。 40 | - =--csl=/path/to/your/csl/chicago-fullnote-bibliography.csl=.指定 =.csl= 文件的路径。你可以从Github的csl文件中任何选择一个。 41 | 42 | - 你可以为 =.docx= 文件选择 =chicago-fullnote-bibliograp-no-ibid.csl=. 43 | 44 | - =--reference-docx=/path/to/your/reference.docx= 是可选项, 但pandoc允许你自定义 =.docx= 风格. 比如,你可以让它看起来跟你的LaTeX风格很接近. 45 | - =-i $1 -o $1-pandoc.docx=. =-i $1= 的意思是使用第一个参数作为输入文件. =-o $1-pandoc.docx= 的意思是它会输出一个文件名一致但添加了 =-pandoc.docx= 后缀的文件. 46 | - 如果你想在转换后自动打开 =.docx=,只需要包含一个 =open $1-pandoc.docx= 文件即可。 47 | -------------------------------------------------------------------------------- /org-mode/整理 org-download.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 整理 org-download 2 | #+URL: https://oremacs.com/2015/01/18/sprucing-up-org-download/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: org-mode 5 | #+DATE: [2020年 02月 21日 星期五 20:17:52 HKT] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | * 整理 org-download 9 | :PROPERTIES: 10 | :CUSTOM_ID: sprucing-up-org-download 11 | :CLASS: post-title 12 | :END: 13 | 14 | 我对[[https://github.com/abo-abo/org-download][org-download]]的兴趣被 =org-mode= 邮件列表中的 [[http://thread.gmane.org/gmane.emacs.orgmode/93964][这个帖子]] 在今天重新激发起来了。 15 | 16 | =org-download= 允许你拖拽一个图像从像Firefox或文件系统到一个 =org-mode= 缓冲区内。 17 | 我最初写这篇文章的时候,正在 [[https://www.edx.org/][edX]] 上学习一门化学课,课上有很多作业和考试,其中包括很多图片。 18 | 在使用 =org-mode= 以文学的方式完成作业时,我希望问题是完全自包含的,为此我需要快速保存网站上的所有图像到 =org-mode= 缓冲区中。 19 | 随着一些hack和资深黑客的建议和贡献, =org-download= 应运而生。 20 | 21 | ** 今天的提交 22 | :PROPERTIES: 23 | :CUSTOM_ID: todays-commits 24 | :END: 25 | 26 | 今天提交的主要贡献是添加了代码取消指向HTML的图像别名。我在Twitter上看到很多人都这样用,[[https://twitter.com/_abo_abo][@_abo_abo]]。这是代码: 27 | 28 | #+begin_src emacs-lisp 29 | (defcustom org-download-img-regex-list 30 | '("= 24.1, build with imagemagick support. 29 | #+END_QUOTE 30 | 31 | (我是直接[[https://savannah.gnu.org/projects/emacs/][从源代码中]] 中构建的Emacs, 并且构建是开启了[[https://imagemagick.org/][ImageMagick]] 支持, 不过我忘了当初是怎么做的了. 貌似只需要安装ImageMagick就行了. 请运行 ~./configure | grep -i imagemagick~ 来看看能否查出相关说明.) 32 | 33 | 我可以在init文件中设置该变量的值: 34 | 35 | #+BEGIN_SRC emacs-lisp 36 | (setq org-image-actual-width nil) 37 | #+END_SRC 38 | 39 | 不过我现在都是把它设置为文件局部变量, 方法是把在Org文件的i第一行加入下面内容: 40 | 41 | #+BEGIN_SRC org 42 | # -*- org-image-actual-width: nil; -*- 43 | #+END_SRC 44 | 45 | 然后, 假设我有如下内容: 46 | 47 | #+BEGIN_SRC org 48 | ,#+NAME: fig:moodleviz 49 | ,#+CAPTION: Screenshot from Moodleviz. 50 | ,#+ATTR_ORG: :width 600 51 | ,#+ATTR_LATEX: :width 5in 52 | [[file:figures/moodleviz-laps.png]] 53 | #+END_SRC 54 | 55 | 该图片原本有1520像素宽(要宽于我的个人笔记本电脑显示宽度—这张图片本身是从一个更大的屏幕上截屏下来的). 若按源大小显示则还需要上下移动该图片才能看清全貌,太麻烦了,所以最好将它缩小一点来显示. 56 | 上面这样设置能让她的宽度缩小到600像素: 57 | 58 | [[https://www.miskatonic.org/images/20160825-attr_org.png]] 59 | 60 | =ATTR_LATEX= 会在我将文档导出成PDF时将图片缩小到指定大小. 我不需要将文档到处成HTML,所以我也不关心设置导出成HTML时的图像大小. 61 | -------------------------------------------------------------------------------- /org-mode/转置org-mode中的表.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 转置org-mode中的表 2 | #+URL: http://pragmaticemacs.com/emacs/transpose-a-table-in-org-mode/ 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: org-mode 5 | #+DATE: [2016-10-11 二 07:37] 6 | #+OPTIONS: ^:{} 7 | 8 | 9 | 我最近需要转置一个Org-mode中的表, 我花了好久时间试图用键盘宏来实现. 然后突然想到,也许org已经内置一条命令来实现这个功能呢. 结果还真被我找到了: =M-x org-table-transpose-table-at-point=. 10 | 下面是一个演示 11 | 12 | [[http://i1.wp.com/pragmaticemacs.com/wp-content/uploads/2016/10/transpose-table.gif?w=620]] 13 | 14 | 有现成的命令的感觉真好! 15 | -------------------------------------------------------------------------------- /org-mode/重整表格数据.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 重整表格数据 2 | #+URL: http://pragmaticemacs.com/emacs/reformatting-tabular-data/ 3 | #+AUTHOR: lujun9972 4 | #+TAGS: org-mode 5 | #+DATE: [2017-06-14 三 16:43] 6 | #+LANGUAGE: zh-CN 7 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 8 | 9 | 10 | 有时候,我需要从pdf论文中抽取出表格中的数据来用于研究. 我可以拷贝表格内容,然后粘帖在Emacs buffer中,然而这些数据的格式一般都需要调整后才能用. 11 | 好在,Emacs有大量的工具可以帮助我们重整这种数据. 12 | 13 | 下面这段gif动画展示了我用来整理数据的那些工具(当然了,还有很多种其他方法可以完成同样的事情). 14 | 15 | [[https://i0.wp.com/pragmaticemacs.com/wp-content/uploads/2017/05/reformat-table.gif?w=620]] 16 | 17 | 在这段动画中,我使用了下面这些工具 18 | 19 | + =C-x h= 选中整个buffer 20 | + =C-c |= 运行 =org-table-create-or-convert-from-region= 将选中区域转换成org表格. 光这样还不够,但至少它让数据变得更好看了. 21 | + =M-S-= 删掉那些不需要的列 22 | + =mc/mark-next-like-this= ([[http://pragmaticemacs.com/emacs/multiple-cursors/][multiple cursors]] 中的函数) 让我同时编辑每一行(我把该功能绑定到了 =M-.=) 23 | + =M-f= 越过下一个单词 24 | + =shrink-whitespace= [[http://pragmaticemacs.com/emacs/delete-blank-lines-and-shrink-whitespace/][删除掉空格]] (我把该功能绑定到了 =M-SPACE=) 25 | + =C-c -= 运行 =org-table-insert-hline= 来为我的表格添加一个水平分割线 26 | 27 | 这些动作将数据重新组织成了我所想要的形式,现在我可以使用 =org-table-export= 将表格导出成其他有用的形式了. 28 | -------------------------------------------------------------------------------- /reddit/如今(2016)编写emacs-lisp的最佳实践是什么.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 如今(2016)编写emacs-lisp的最佳实践是什么 2 | #+URL: https://www.reddit.com/comments/43nh3h 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: reddit 5 | #+DATE: [2016-09-06 二 21:54] 6 | #+OPTIONS: ^:{} 7 | 8 | + Rejeep编写了许多现代化的工具,因此你应该多看看他的博客,尤其是[[http://rejeep.github.io/emacs/cask/ert/ert-runner/ert-async/ecukes/testing/travis/2014/01/09/various-testing-tools-in-emacs.html][这篇文章]] 9 | 10 | + 你可以仿照[[https://github.com/davidshepherd7/electric-operator][这个package]] 复用它的 =Makefile=, =Cask=, =.travis.yml= 文件以及它的package heade 11 | 12 | + 如果你的package没有太复杂的UI,可以用[[https://github.com/ecukes/ecukes][ecukes]] 来写集成测试. 13 | 14 | + 借助Travis来帮你在不同版本的emacs(使用evm)下编译(检查是否有warning)并测试你的package. 15 | 16 | + 用 =ert= 对lisp函数作单元测试. 17 | 18 | + 使用 =Cask= 管理依赖(这样一来,你使用Travis就更方便了,并且你开发时也不用全局安装依赖包了. 19 | 20 | + =Names= 这个package挺不错的,但是我觉得 =nameless= 更不错. 21 | 22 | + 最好不要用melpa unstable上的pacakge,尽量用melpa stable上的package代替. 23 | 24 | + [[https://github.com/magnars/dash.el][dash]], [[https://github.com/magnars/dash.el][s]], 等package都很不错,而且借助Cask与package.el,我们可以很方便的使用这些package提供的函数. 但是也推荐你优先使用[[http://endlessparentheses.com/new-on-elpa-and-in-emacs-25-1-seq-el.html][seq.el]], [[http://emacsredux.com/blog/2014/02/02/a-peek-at-emacs-24-dot-4-new-string-manipulation-functions/][subr-x.el]] 中新增的类似功能的函数. 25 | 26 | + 尽量使用静态作用域,方法是将 =;;; -*- lexical-binding: t; -*-= [[http://irreal.org/blog/?p=628][放到文件的第一行位置]]. 27 | 28 | + 将你的package发布到melpa或marmalade上,与别人分享. 29 | -------------------------------------------------------------------------------- /reddit/如何将一段文本变成org-mode中的列表.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 如何将一段文本变成org-mode中的列表 2 | #+AUTHOR: lujun9972 3 | #+TAGS: reddit,org 4 | #+DATE: [2016-11-29 二 20:05] 5 | #+LANGUAGE: zh-CN 6 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 7 | 8 | 假设有这么一段文本 9 | #+BEGIN_SRC org 10 | thing one 11 | thing two 12 | thing three 13 | etc 14 | #+END_SRC 15 | 16 | 那么先全选在通过 =C-c -= 转换成 17 | #+BEGIN_SRC org 18 | - thing one 19 | - thing two 20 | - thing three 21 | - etc 22 | #+END_SRC 23 | 24 | 也可以通过先全选,再通过 =C-u C-x r N RET DEL . SPC RET= 转换成 25 | #+BEGIN_SRC org 26 | 1. thing one 27 | 2. thing two 28 | 3. thing three 29 | 4. etc 30 | #+END_SRC 31 | -------------------------------------------------------------------------------- /reddit/如何更改org-mode中TODO关键字的颜色.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 如何更改org-mode中TODO关键字的颜色 2 | #+AUTHOR: lujun9972 3 | #+TAGS: reddit 4 | #+DATE: [2016-12-23 五 14:26] 5 | #+LANGUAGE: zh-CN 6 | #+OPTIONS: H:6 num:nil toc:t \n:nil ::t |:t ^:nil -:nil f:t *:t <:nil 7 | 8 | 1. 你可以通过设置 =org-todo-keywords= 来自定义TODO关键字 9 | 10 | #+BEGIN_SRC emacs-lisp 11 | (setq org-todo-keywords 12 | '((sequence "TODO" "IN-PROGRESS" "WAITING" "|" "DONE" "CANCELED")) 13 | ) 14 | #+END_SRC 15 | 16 | 17 | 2. 你可以通过设置 =org-todo-keyword-faces= 来自定义各个TODO关键字的显示 18 | 19 | #+BEGIN_SRC emacs-lisp 20 | (setq org-todo-keyword-faces 21 | '(("IN-PROGRESS" . "orange") ("WAITING" . "magenta") ("CANCELED" . "red") ("DONE" . "green")) 22 | ) 23 | ;; 或者这样 24 | (setq org-todo-keyword-faces (quote (("IN-PROGRESS" :foreground "orange")))) 25 | #+END_SRC 26 | 27 | 关于 =org-todo-keyword-faces= 的设置方式,请参见它的doc-string. 28 | -------------------------------------------------------------------------------- /reddit/如何让Emacs在运行期变得更快一点.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 如何让Emacs在运行期变得更快一点 2 | #+URL: https://www.reddit.com/comments/3l824y 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: reddit 5 | #+DATE: [2016-08-31 三 14:35] 6 | #+OPTIONS: ^:{} 7 | 8 | + 有些package的性能很糟糕,最后不要用,比如 =linum-mode=, =flyspell-mode=, =tabbar-mode=. 9 | + 如果你经常打开很多的文件,不妨试试设置一下 =jit-lock-stealth-time=, 10 | + 在Hook中做太多的事情也是造成缓慢的常见原因 11 | + 用 =profiler= 找出慢的地方. 方法是: 12 | 1. 执行 =M-x profiler-start= 13 | 2. 执行那些会导致Emacs缓慢的操作 14 | 3. 执行 =M-x profiler-report=, 它会告诉你什么地方缓慢. 15 | 4. 修复缓慢的原因. 16 | 5. 在解决下一个缓慢因素之前,记得执行 =M-x profiler-stop= 17 | -------------------------------------------------------------------------------- /reddit/如何配置Tramp使得只需要输入一次密码就可以让Emacs把远程服务器当成本地服务器那样来用.org: -------------------------------------------------------------------------------- 1 | #+TITLE: 如何配置Tramp使得只需要输入一次密码就可以让Emacs把远程服务器当成本地服务器那样来用 2 | #+URL: https://www.reddit.com/comments/3liwm7 3 | #+AUTHOR: lujun9972 4 | #+CATEGORY: emacs-common 5 | #+DATE: [2016-08-31 三 14:24] 6 | #+OPTIONS: ^:{} 7 | 8 | 你只需要启用ssh的 connection sharing功能就行了. 方法是编辑本地的 =~/.ssh/config= 文件然后添加下面这些配置: 9 | 10 | #+BEGIN_SRC conf 11 | Host * 12 | # Enable connection sharing. 13 | ControlMaster auto 14 | ControlPath ~/.ssh/%r@%h:%p 15 | # Make persistent connections that can be reused. 16 | ControlPersist yes 17 | # Keep connections alive (helps TRAMP to detect disconnects.) 18 | ServerAliveInterval 5 19 | #+END_SRC 20 | 21 | 然后在Emacs中配置参数 =tramp-use-ssh-controlmaster-options= 为 =nil= 22 | -------------------------------------------------------------------------------- /script/0_sync_master.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | source base.sh 4 | 5 | cd "$(get-project-path)" 6 | if ! git-branch-exist-p "PROJECT";then 7 | git remote add PROJECT https://github.com/lujun9972/emacs-document.git 8 | fi 9 | 10 | git fetch PROJECT 11 | git pull PROJECT master:master 12 | git push origin master:master 13 | -------------------------------------------------------------------------------- /script/1_add_new_article_manual.sh: -------------------------------------------------------------------------------- 1 | 1_add_new_article_newspaper.sh -------------------------------------------------------------------------------- /script/1_add_new_article_newspaper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | source $(dirname "${BASH_SOURCE[0]}")/base.sh 4 | 5 | url="$*" 6 | [[ -z "${url}" ]] && read -r -p "please input the URL:" url 7 | baseurl=$(get-domain-from-url "${url}") 8 | if url-blocked-p "${baseurl}";then 9 | warn "${baseurl} is blocked!" 10 | exit 1 11 | fi 12 | 13 | # 搜索类似的文章 14 | echo "search simliar articles..." 15 | if search-similar-articles "${url}";then 16 | continue-p "found similar articles" 17 | fi 18 | 19 | parse_url_script="parse_url_by_${0##*_}" 20 | response=$(${CFG_PATH}/${parse_url_script} "${url}") 21 | 22 | # 获取title 23 | if [[ -z "${title}" ]];then 24 | title=$(echo ${response} |jq -r .title) 25 | [[ "${title}" == "null" || -z "${title}" ]] && read -r -p "please input the Title:" title 26 | fi 27 | 28 | # 获取content 29 | content=$(echo ${response} |jq -r .content) 30 | 31 | echo title= "${title}" 32 | echo content= "${content}" 33 | 34 | # 生成新文章 35 | source_path="../raw" 36 | filename=$(date-title-to-filename "${title}") 37 | source_file="${source_path}"/"${filename}" 38 | 39 | # 使用trap删掉临时文件 40 | function cleanup_temp { 41 | [ -e "${source_file}" ] && rm --force "${source_file}" 42 | exit 0 43 | } 44 | trap cleanup_temp SIGHUP SIGINT SIGPIPE SIGTERM 45 | 46 | 47 | cat > "${source_file}"<> "${source_file}" 59 | 60 | else 61 | pandoc --reference-links --reference-location=document -f html-native_divs-native_spans -t org --wrap=preserve --strip-comments --no-highlight --indented-code-classes=python ${url} >> "${source_file}" 62 | fi 63 | 64 | 65 | eval "$(get-editor) '${source_file}'" 66 | read -p "保存好原稿后,按回车继续" continue 67 | 68 | # 新建branch 并推送新文章 69 | filename=$(basename "${source_file}") 70 | new_branch="$(filename-to-branch add "${filename}")" 71 | git branch "${new_branch}" master 72 | git checkout "${new_branch}" 73 | git add "${source_file}" 74 | git commit -m "选题: ${title}" && git push -u origin "${new_branch}" 75 | -------------------------------------------------------------------------------- /script/2_start_translating.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | source $(dirname "${BASH_SOURCE[0]}")/base.sh 4 | # 搜索可以翻译的文件 5 | declare -a files 6 | sources_dir="$(get-project-path)/raw" 7 | file="$*" 8 | 9 | 10 | cd "$(dirname "${file}")" 11 | filename=$(basename "${file}") 12 | new_branch="$(filename-to-branch translate "${filename}")" 13 | git branch "${new_branch}" master 14 | git checkout "${new_branch}" 15 | # 迁移到process目录 16 | git mv "${filename}" "../processing" 17 | git add "../processing/${filename}" 18 | git_user=$(get-github-user) 19 | git commit -m "translating by ${git_user}" 20 | git push -u origin "${new_branch}" 21 | 22 | # 打开要翻译的文章 23 | eval "$(get-editor) '../processing/${filename}'" 24 | -------------------------------------------------------------------------------- /script/3_continue_the_work.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | source $(dirname "${BASH_SOURCE[0]}")/base.sh 4 | 5 | cd "$(get-project-path)" 6 | current_branch=$(git-get-current-branch) 7 | if [[ "$current_branch" == "master" ]];then 8 | i=0 9 | while read branch;do 10 | operation=$(git-branch-to-operation "${branch}") 11 | filename=$(git-branch-to-filename "${branch}") 12 | printf "%3d. %10s %s\n" $i "${operation}" "${filename}" 13 | branches[$i]=$branch 14 | i=$((i+1)) 15 | done < <(git branch |grep -v 'master') 16 | echo "YOU ARE UNDER BRANCH MASTER" 17 | read -r -p "Which branch do you want to checkout(input the id): " num 18 | echo git checkout "${branches[$i]}" 19 | git checkout "${branches[$num]}" 20 | fi 21 | 22 | filepath=$(git-current-branch-to-file-path) 23 | eval "$(get-editor) '${filepath}'" 24 | -------------------------------------------------------------------------------- /script/4_finish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source $(dirname "${BASH_SOURCE[0]}")/base.sh 3 | while getopts :dm: OPT; do 4 | case $OPT in 5 | d|+d) 6 | delete_branch=1 7 | ;; 8 | m|+m) 9 | commit_message="$OPTARG" 10 | ;; 11 | *) 12 | echo "usage: ${0##*/} [+-d}" 13 | exit 2 14 | esac 15 | done 16 | shift $(( OPTIND - 1 )) 17 | OPTIND=1 18 | 19 | cd $(get-project-path) 20 | current_branch=$(git-get-current-branch) 21 | if [[ "${current_branch}" == "master" ]];then 22 | warn "Project is under the master branch! Exiting" 23 | exit 1 24 | fi 25 | 26 | operation=$(git-branch-to-operation "${current_branch}") 27 | filename=$(git-branch-to-filename "${current_branch}") 28 | 29 | if [[ "${operation}" == "translate" ]];then 30 | # 使用reformat.sh格式化 31 | reformat_flag=$(get-cfg-option AutoReformat) 32 | if [[ -n "${reformat_flag}" && "${reformat_flag}" != "0" ]];then 33 | echo "reformat the ${filename}" 34 | (cd $CFG_PATH;./reformat.sh) 35 | fi 36 | 37 | # 将文件从sources移动到translated目录 38 | sources_file_path=$(find ./ -name "${filename}") # 搜索出相对路径 39 | if [[ "${sources_file_path}" =~ ^\./sources/.+$ ]];then 40 | translated_file_path="$(echo "${sources_file_path}"|sed 's/sources/translated/')" 41 | echo git mv "${sources_file_path}" "${translated_file_path}" 42 | git mv "${sources_file_path}" "${translated_file_path}" 43 | fi 44 | fi 45 | 46 | if [[ -z "${commit_message}" ]];then 47 | commit_message="${operation} done: ${filename}" 48 | fi 49 | 50 | git add . 51 | git commit -m "${commit_message}" && git push -u origin "${current_branch}" 52 | git checkout master 53 | if [[ -n "${delete_branch}" ]];then 54 | git branch -d "${current_branch}" 55 | fi 56 | -------------------------------------------------------------------------------- /script/5_pause.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source $(dirname "${BASH_SOURCE[0]}")/base.sh 3 | while getopts :m: OPT; do 4 | case $OPT in 5 | m|+m) 6 | commit_message="$OPTARG" 7 | ;; 8 | *) 9 | echo "usage: ${0##*/} [+-m commit_message]" 10 | exit 2 11 | esac 12 | done 13 | shift $(( OPTIND - 1 )) 14 | OPTIND=1 15 | 16 | cd $(get-project-path) 17 | current_branch=$(git-get-current-branch) 18 | if [[ "${current_branch}" == "master" ]];then 19 | warn "Project is under the master branch! Exiting" 20 | exit 1 21 | fi 22 | 23 | # operation=$(git-branch-to-operation "${current_branch}") 24 | filename=$(git-branch-to-filename "${current_branch}") 25 | 26 | 27 | if [[ -z "${commit_message}" ]];then 28 | commit_message="take a break: ${filename} at $(date)" 29 | fi 30 | 31 | git add . 32 | git commit -m "${commit_message}" 33 | git push -u origin "${current_branch}" 34 | git checkout master 35 | -------------------------------------------------------------------------------- /script/auto_translate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | source base.sh 4 | export PATH=$PATH:${CFG_PATH} 5 | while getopts :rf: OPT; do 6 | case $OPT in 7 | r|+r) 8 | replace_flag="True" 9 | ;; 10 | f|+f) 11 | source_file="$OPTARG" 12 | ;; 13 | *) 14 | echo "usage: ${0##*/} [+-rf ARG} [--] ARGS..." 15 | exit 2 16 | esac 17 | done 18 | shift $(( OPTIND - 1 )) 19 | OPTIND=1 20 | 21 | if [[ "${replace_flag}" == "True" ]];then 22 | dest_file="${source_file}.autotranslated" 23 | else 24 | dest_file="/dev/tty" 25 | fi 26 | 27 | function do_translate() 28 | { 29 | article="$*" 30 | position="元数据" # 其他可能值包括 "正文","引用","结尾" 31 | while read line 32 | do 33 | echo "${line}" 34 | if [[ "${position}" == "元数据" ]];then 35 | if [[ "${line}" == \#+* ]];then 36 | continue 37 | else 38 | position="正文" 39 | fi 40 | fi 41 | 42 | if [[ "${position}" != "属性" && "${line^^}" =~ ^[[:blank:]]*':PROPERTIES:' ]];then 43 | position="属性" 44 | continue 45 | fi 46 | 47 | if [[ "${position}" == "属性" && "${line^^}" =~ ^[[:blank:]]*':END:' ]];then 48 | position="正文" 49 | continue 50 | fi 51 | 52 | if [[ "${line}" =~ ^\*+[[:blank:]] ]];then # 标题不翻译 53 | continue 54 | fi 55 | 56 | 57 | if [[ "${position}" != "引用" && "${line^^}" =~ ^[[:blank:]]*'#+BEGIN_EXAMPLE' ]];then 58 | position="引用" 59 | continue 60 | fi 61 | 62 | if [[ "${position}" == "引用" && "${line^^}" =~ ^[[:blank:]]*'#+END_EXAMPLE' ]];then 63 | position="正文" 64 | continue 65 | fi 66 | 67 | if [[ "${position}" != "引用" && "${line^^}" =~ ^[[:blank:]]*'#+BEGIN_SRC' ]];then 68 | position="引用" 69 | continue 70 | fi 71 | 72 | if [[ "${position}" == "引用" && "${line^^}" =~ ^[[:blank:]]*'#+END_SRC' ]];then 73 | position="正文" 74 | continue 75 | fi 76 | 77 | if [[ "${position}" == "正文" && "${line}" == *[a-zA-Z]* ]];then 78 | printf "翻译:" 79 | fanyi.sh "${line}" # 至少包含一个英文字母才需要翻译 80 | fi 81 | done < <(cat "${article}") 82 | } 83 | 84 | do_translate "${source_file}"|tee "${dest_file}" 85 | -------------------------------------------------------------------------------- /script/fanyi.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | set -o errexit 4 | content="请将我的非中文文字翻译成中文!注意翻译时保持源文本格式不变,遇到不认识的单词那可能是专有词汇,保持源文不翻译。另外不要做无畏的演绎!:$*" 5 | # BASEURL="https://api.deepseek.com" 6 | # MODEL="deepseek-chat" 7 | # messages="[{\"role\": \"system\", \"content\": \"你是一个好用的翻译助手,请将我的非中文文字翻译成中文。\"}, 8 | # {\"role\": \"user\", \"content\": \"${content}\"} 9 | # ]" 10 | 11 | # PROMPT='你是一个好用的翻译助手,请将我的英文文字翻译成中文,非英文不用翻译,翻译后请保持org-mode格式不变!".' 12 | PROMPT='你是一个好用的翻译助手".' 13 | BASEURL="https://open.bigmodel.cn/api/paas/v4" 14 | MODEL="glm-4v-flash" 15 | messages=$(jq -n --arg content "${content}" --arg prompt "$PROMPT" '[{"role": "assistant", "content": $prompt}, 16 | {"role": "user", "content": [{"type": "text", "text": $content}]} 17 | ]') 18 | selector=".choices[0].message.content| @text" 19 | # echo $messages 20 | curl -s "${BASEURL}/chat/completions" \ 21 | -H "Content-Type: application/json" \ 22 | -H "Authorization: Bearer ${LLMKEY}" \ 23 | -d "{ 24 | \"model\": \"${MODEL}\", 25 | \"messages\": ${messages}, 26 | \"stream\": false 27 | }" |jq -r "${selector}" 28 | -------------------------------------------------------------------------------- /script/parse_url_by_manual.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | url="$*" 3 | function trim() 4 | { 5 | echo "$*" |sed 's/^[[:space:]]*//;s/[[:space:]]*$//' 6 | } 7 | source=$(curl "$url") 8 | title=$(trim $(echo "${source}" |grep -i ''|sed 's$.*<title>$$i;s$.*$$i')) 9 | echo '{}'|jq '{"title":$title,"content":$source}' --arg title "$title" --arg source "$source" 10 | -------------------------------------------------------------------------------- /script/parse_url_by_newspaper.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import datetime 3 | import json 4 | from newspaper import Article 5 | url = sys.argv[1] 6 | 7 | article = Article(url, keep_article_html=True, memoize_articles=False, follow_meta_refresh=True, request_timeout=15, fetch_images=False) 8 | article.download() 9 | article.parse() 10 | 11 | authors = ';'.join(article.authors) 12 | title = article.title 13 | date = article.publish_date 14 | if date: 15 | date = date.strftime('%Y%m%d') 16 | 17 | html_content = article.article_html 18 | content = article.text 19 | 20 | data = {"title": title, 21 | "date_published": date, 22 | "content": html_content, 23 | "author": authors} 24 | 25 | print(json.dumps(data)) 26 | -------------------------------------------------------------------------------- /script/parse_url_by_newspaper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | PYTHON="python" 3 | set -e 4 | cd $(dirname "${BASH_SOURCE[0]}") 5 | source base.sh 6 | 7 | url="$*" 8 | 9 | python_env=$(get-cfg-option PythonEnv) 10 | if [[ -d "${python_env}" ]];then 11 | PYTHON="${python_env}/bin/python" 12 | fi 13 | 14 | $PYTHON parse_url_by_newspaper.py "${url}" 15 | -------------------------------------------------------------------------------- /script/project.cfg: -------------------------------------------------------------------------------- 1 | ProjectRoot=/home/lujun9972/github/emacs-document 2 | GithubUser=lujun9972 3 | Browser=firefox ---new-tab 4 | Editor=emacsclient -a '' -c 5 | BlockedDomains=https://www.tecmint.com| 6 | Token=kKczNLzhY6jM60hv03IFjew9TQ7WH245CACYaBiY 7 | PythonEnv=env 8 | AutoReformat=0 9 | ParseUrlScript=parse_url_by_manual.sh -------------------------------------------------------------------------------- /script/reformat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 使用全角中文 3 | source $(dirname "${BASH_SOURCE[0]}")/base.sh 4 | if [[ $# -eq 0 ]];then 5 | cd "$(get-lctt-path)" 6 | filepath=$(git-current-branch-to-file-path) 7 | else 8 | filepath="$*" 9 | fi 10 | 11 | sed -i '1,/----------------------------------------------------------------/ { 12 | /```/,/```/!{ 13 | # 中英文之间加上空格,英文可能被符号扩起来了 14 | s/\([[:upper:][:lower:]][[:punct:]]*\)\([^[:upper:][:lower:][:blank:][:cntrl:][:punct:][:digit:]]\)/\1 \2/g; 15 | s/\([^[:upper:][:lower:][:blank:][:cntrl:][:punct:][:digit:]]\)\([[:punct:]]*[[:upper:][:lower:]]\)/\1 \2/g; 16 | # 中文和数字之间加上空格,数字也可能被符号扩起来了 17 | s/\([[:digit:]][[:punct:]]*\)\([^[:upper:][:lower:][:blank:][:cntrl:][:punct:][:digit:]]\)/\1 \2/g; 18 | s/\([^[:upper:][:lower:][:blank:][:cntrl:][:punct:][:digit:]]\)\([[:punct:]]*[[:digit:]]\)/\1 \2/g; 19 | s/,/,/g; # 任何,都被替换 20 | s/?/?/g; # 任何?都被替换 21 | s/!$/!/g; # !在尾部则可被替换 22 | s/!\([^[]\)/!\1/g; # !不跟[则可以被替换,但是由于图片的格式是![,因此不能被替换 23 | # 由于.用户划分域名(www.baidu.com)和版本号(3.4.1),因此不能被随意替换 24 | s/\.$/。/g; # .在行尾也能被替换 25 | s/\([^[:space:]]\)\.\([^[:upper:][:lower:][:digit:]]\)/\1。\2/g; # .前不是空格且后面不是英文或数字也能被替换 26 | # 由于:用户划分域名(http://www.baidu.com),因此也不能被随意替换 27 | s/:$/:/g; # :在行尾也能被替换 28 | s/:\([^/]\)/:\1/g; # :后面不跟/则能被替换 29 | s/:\([^[:upper:][:lower:][:digit:][:punct:]]\)/:\1/g; # :后不是英文或数字或标点也能被替换 30 | # 全角标点与其他字符之间不加空格 31 | s/[[:blank:]]*\(:\|,\|?\|!\|。\)/\1/g; 32 | s/\(:\|,\|?\|!\|。\)[[:blank:]]*/\1/g; 33 | }}' "${filepath}" 34 | -------------------------------------------------------------------------------- /script/texput.log: -------------------------------------------------------------------------------- 1 | This is TeXk, Version 3.14159265 (TeX Live 2017/Arch Linux) (preloaded format=tex 2017.11.23) 2 MAR 2018 14:44 2 | restricted \write18 enabled. 3 | %&-line parsing enabled. 4 | **&tex 5 | 6 | *\end 7 | No pages of output. 8 | -------------------------------------------------------------------------------- /script/urls_checker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | function Usage 3 | { 4 | cat <