做异端中的异端 -- Emacs裸奔之路5: 条件反射式移动

news2024/12/5 9:59:36

移动命令使用频率非常之高,只要方法多一个小小的弯路,对使用体验影响都很大。
克服移动上的难度,离掌握Emacs就不远了。

在不安装其它包的情况下,Emacs就可以:

  • 以行为单位移动: C-n/C-p
  • 以段落为单位移动:C-Down/C-Up

但能快速移动到某一行,这种方式太低效了,就算是使用C-r/C-s搜索字符,也非常的麻烦。

本文以上下移动命令为例,利用人的本能条件反射,实现高效的移动方式。

基本思想

  • 利用本能反应,即目标在哪就往哪里移动
  • 对不精确的操作再修正

具体步骤是:

  1. 目标在光标上方,就向上移动,反之向下
  2. 移动的幅度为当前位置到边界的一半
  3. 再重复1,2步骤,直到满意为止

整个过程利用二分法定位。

动图演示,当前光标在文件底部,目标位置是NOTE那行注释:

在这里插入图片描述

avy之类的第三方包有类似的功能,但思想完全不一样。

它些包基本是为每行生成一些标记,再通过输入标记从而定位到对应的位置。

这些方法你需要观察标记,再输入标记。但读取标记是要经过大脑思考的,并不是人的本能反应,所以使用起来并不流畅。

类比于开车转弯:
当想向右拐弯的时候,向右打方向盘是不用思考的,只是打多打少的问题。如果车辆打过了,再向左打以校正,重复这个过程直到车辆方向正确。
这个过程是非常自然的,人脑不是擅长这种精密操作,有一个八九不离十再修正就可以了。

具体实现

Margin处的颜色与字符是用来预测下两步操作的,选择JK是因为这个思想我先在Vim上实现,JK是Vim的移动方式。
Margin上的标识比Vim中的Sign方便,不像Vim最多两个字符,而且在Bash上也可以正常渲染。
关键代码并不多,大部分是细节优化,比如到达边界自动翻页、跳转到上次位置、跳过折叠的内容等。

  (global-set-key (kbd (concat custom-user-prefix-key "k")) (lambda () (interactive) (custom/bisect-jump-2 t)))
  (global-set-key (kbd (concat custom-user-prefix-key "j")) 'custom/bisect-jump-2)

(defun custom/bisect-jump-2 ( &optional is-up )
  "Bisect jump."
  (interactive)

  (setq old-hl-line-flag nil)
  (if (boundp 'hl-line-mode)
      (setq old-hl-line-flag hl-line-mode)
    )

  (create-all-bisect-jump-marks)
  (set-window-margins nil 2)
  (setq hit-radus 0)

  (setq current-position (count-screen-lines (window-start) (point)))
  (setq top-line 1)
  (setq bottom-line (- (count-screen-lines (window-start) (window-end)) 1))

  (setq done nil)
  (setq old-position (point))
  (setq reach-end nil)
  (setq move-down nil)
  (setq move-up nil)
  (setq is-quick nil)

  (while (not done)

    (setq old-position (point))

    (if reach-end
        (progn
          (setq top-line 1)
          (setq bottom-line (- (count-screen-lines (window-start) (window-end)) 1))          
          (setq reach-end nil)))

    ;; (if (<= (1+ (* 2 hit-radus)) (- bottom-line top-line))
    (if (and (> bottom-line top-line) (not move-down) (not move-up))
        (if is-up
            (progn
              (setq bottom-line (- (- current-position hit-radus) 1) )
              (if is-quick
                  (setq top-line (/ (+ top-line bottom-line) 2))))

            (progn
              (setq top-line (+ current-position hit-radus 1))
              (if is-quick
                  (setq bottom-line (/ (+ top-line bottom-line) 2))))))


    (if (>= top-line bottom-line)
        (progn
          (if is-up            
              (setq bottom-line top-line)
            (setq top-line bottom-line))
          (setq reach-end t)))

    (setq flags (generate-flags (1+ (- bottom-line top-line)) (+ 1 (* 2 hit-radus))))

    (if (and (not move-down) (not move-up))
        (setq current-position (/ (+ top-line bottom-line) 2))

      (progn
        (if move-down
            (setq current-position (1+ current-position))
          (setq current-position (1- current-position))
          )
        )
      )

    (goto-char (window-start))
    (vertical-motion (- current-position 1))

    ;; (global-hl-line-highlight)
    (hl-line-mode)

    (clear-all-bisect-jump-marks)

    ;; render the position
    (setq i 0)
    (while (< i (length flags))
           (setq item (nth i flags))
           (setq line (+ i top-line))
           (setq position (custom/pos-at-screen-line-col line 0))

           (if (or (= 0 (nth 0 item)) (> 2 (length item)))
             (mark-in-margin i position (nth 0 item) 0)
             (progn
               (setq first-element (nth 0 item))
               (setq second-element (nth 1 item))
               (mark-in-margin i position first-element second-element)
               )
             )
           (setq i (1+ i)))

    (if (and (= old-position (point)) reach-end)
        (progn
          (setq top-line 1)
          (setq bottom-line (- (count-screen-lines (window-start) (window-end)) 1))

          (if is-up                      
              (recenter (- bottom-line 2) t)
              (recenter 1 t)
            )

          (setq current-position (/ (+ top-line bottom-line) 2))
          (setq top-line current-position)
          (setq bottom-line current-position)
          )
      )

    (if reach-end
        (message "Reach the finish line, there's no room to jump yet!")
      (message "Bisect jump. Type j/k or M-j/k to jump up or down, ' to jump back and 'q' to quit."))

    (setq move-down nil)
    (setq move-up nil)
    (setq is-quick nil)
    (setq key 0)

    ;; https://stackoverflow.com/questions/884498/how-do-i-intercept-ctrl-g-in-emacs    
    (let ((inhibit-quit t))

      (unless (with-local-quit
                (setq key (read-key "read-key"))
                ;; read-event is affected by message statement, quite weird.
                ;; (setq key (read-event "todo")) 
                t)
        (progn
          (setq key ?q)
          (message "C-g hit")
          (setq quit-flag nil)
          )))

    (cond
     ( (equal key ?j)
      (setq is-up nil))
     ( (equal key ?k)
       (setq is-up t))
     ( (equal key ?n)
       (setq is-quick t)
       (setq is-up nil))
     ( (equal key ?p)
       (setq is-quick t)
       (setq is-up t))          
     ( (equal key ?q)
       (setq done t))
     ( (or (equal key ?') (equal key ?\;) )
       (goto-char old-position)
       (setq done t))
     (t
      (setq done t)

      ;; apply the current key press 
      (setq unread-command-events
        (append (listify-key-sequence (vector key))
            unread-command-events))

      )
     )

    (if done
        (progn
          (clear-all-bisect-jump-marks)
          (message "Quit bisect-jump.")
          ))

    )

  (unless old-hl-line-flag
    (hl-line-mode -1)
    )
  )

其中generate-flags是用于计算预测位置的:

(defun generate-flags (num  width)

  (let
    ((flags '()) 
     (flags-1 '()) 
     (pos1 0)
     (pos2 0)
     (i 0))

    (if (<= num width) 
      (setq flags (make-list num '(0)))
      (progn

        (setq pos1 (/ (- num width) 2))
        (setq pos2 (+ pos1 width))

        ; first part [0, pos1)
        (setq flags (make-list pos1 '(1)))
        (if (> pos1 width) 
          (progn
            (setq flags-1 (generate-flags pos1 width))
            (setq i 0)
            (while (< i pos1)
                   (setf (elt flags i) (append (nth i flags) (nth i flags-1)) )
                   (setq i (1+ i))
                   )
            )
          )

        ; middle part [pos1, pos2)
        (setq flags (append flags (make-list (- pos2 pos1) '(0))))

        ; last part [pos2, num)
        (setq flags (append flags 
                            (make-list (- num pos2) '(2))
                            ))
        (if (> (- num pos2) width) 
          (progn
            (setq flags-1 (generate-flags (- num pos2) width))
            (setq i 0)
            (while (< i (- num pos2))
                   (setf (elt flags (+ pos2 i)) (append (nth (+ pos2 i) flags) (nth i flags-1)) )
                   (setq i (1+ i))
                   )
            )
          )


        )
      )

    flags
    )
)

至此,你不需要安装额外的包就解决了光标移动的效率问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2252654.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于单片机的WIFI、语音、储存、时钟、闹钟、定位系统

所有仿真详情导航&#xff1a; PROTEUS专栏说明-CSDN博客 目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;采用DS1302时钟模块&#xff0c;通过LCD1602显示实时时间&#xff0c;也可以储存时间在AT2DC02中&#xff0c…

【阅读记录-章节4】Build a Large Language Model (From Scratch)

文章目录 4. Implementing a GPT model from scratch to generate text4.1 Coding an LLM architecture4.1.1 配置小型 GPT-2 模型4.1.2 DummyGPTModel代码示例4.1.3 准备输入数据并初始化 GPT 模型4.1.4 初始化并运行 GPT 模型 4.2 Normalizing activations with layer normal…

[创业之路-158]:《BLM战略规划》BLM业务业务领先模型与从战略到执行-模型(DSTE)

目录 一、BLM业务业务领先模型 - 整体框架 1.1 战略制定 1.2 战略执行 二、战略领导力&#xff1a;洞察&#xff08;看的远、看得深&#xff09;、决断&#xff08;看得清&#xff09;、执行力&#xff08;做得快、做得好&#xff09; 2.1 基本框架&#xff1a;战略制定、…

配置 Android Studio cursor/vscode 环境(切换 Flutter 版本)

系统环境变量 Path Android Studio 配置 Java 系统变量 Path PS&#xff1a;

Qt几何数据类型:QPoint类型详解(基础向)

目录 QPoint类 QPoint的构造 QPoint公有方法 isNull() rx() ry() setX() setY() toCGPoint() toPointF() transposed() x() y() manhattanLength() 各类重载运算符 QPoint的静态方法 dotproduct() QPoint类 在 Qt 框架中&#xff0c;QPoint 是一个简单且常用的类&…

外卖开发(三)开发笔记——AOP实现实现公共字段填充、主键回显、抛异常和事务管理

外卖开发&#xff08;三&#xff09;开发笔记 一、AOP实现实现公共字段填充&#xff08;减少重复工作&#xff09;实现思路自定义注解AutoFill自定义切面AutoFillAspect在Mapper接口上添加AutoFill注解 二、主键回显情况三、抛异常 和 事务管理 一、AOP实现实现公共字段填充&am…

Flutter 1.2:flutter配置gradle环境

1、在android的模块中进行gradle环境配置 ①在 gradle-wrapper.properties文件中将url配置为阿里云镜像&#xff0c;因为gradle的服务器在国外&#xff0c;国内下载非常慢&#xff0c;也可在官网进行下载 gradle版本下载 gradle版本匹配 阿里云镜像gradle下载 可以通过复制链…

神经网络入门实战:(九)分类问题 → 神经网络模型搭建模版和训练四步曲

(一) 神经网络模型搭建官方文档 每一层基本都有权重和偏置&#xff0c;可以仔细看官方文档。 pytorch 官网的库&#xff1a;torch.nn — PyTorch 2.5 documentation Containers库&#xff1a;用来搭建神经网络框架&#xff08;包含所有的神经网络的框架&#xff09;&#xff1b…

达梦数据库文件故障的恢复方法

目录 1、概述 1.1 概述 1.2 环境介绍 2、使用备份集的恢复方法 2.1 实验准备 2.2 误删除“用户表空间数据文件” 2.3 误删除SYSTEM.DBF 2.4 误删除ROLL.DBF 2.5 REDO日志文件 3、无备份集的恢复方法 3.1 误删除“表空间数据文件” 3.2误删除控制文件 3.3 误删除RO…

uniapp进阶技巧:如何优雅地封装request实例

在uniapp开发过程中&#xff0c;合理封装网络请求是提高代码质量和开发效率的关键。本文将介绍一种更为优雅的封装方式&#xff0c;通过创建一个request实例来管理不同类型的HTTP请求。 一、准备工作 在开始封装之前&#xff0c;请确保你的项目中已经安装了uniapp开发环境&…

45 基于单片机的信号选择与温度变化

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;采用DS18B20检测温度&#xff0c;通过三种LED灯代表不同状态。 采用DAC0832显示信号脉冲&#xff0c;通过8位数码管显示温度。 信号脉冲可以根据两个按键分别调整为正弦…

yagmail邮件发送库:如何用Python实现自动化邮件营销?

&#x1f3a5; 作者简介&#xff1a; CSDN\阿里云\腾讯云\华为云开发社区优质创作者&#xff0c;专注分享大数据、Python、数据库、人工智能等领域的优质内容 &#x1f338;个人主页&#xff1a; 长风清留杨的博客 &#x1f343;形式准则&#xff1a; 无论成就大小&#xff0c;…

付费版-多媒体云转码视频处理工具-付费系统完整源码搭建-先到先得-本文带完整搭建步骤-优雅草央千澈

付费版-多媒体云转码视频处理工具-付费系统完整源码搭建-先到先得-本文带完整搭建步骤-优雅草央千澈 环境 linuxnginxphp7.1mysql5.6 安装步骤 以下适用于宝塔已经安装好的情况 1.上传源码到你的网站目录 2.访问你的域名&#xff0c;按操作提示进行安装配置&#xff08;如…

java基础概念46-数据结构1

一、引入 List集合的三种实现类使用了不同的数据结构&#xff01; 二、数据结构的定义 三、常见的数据结构 3-1、栈 特点&#xff1a;先进后出&#xff0c;后进先出。 java内存容器&#xff1a; 3-2、队列 特点&#xff1a;先进先出、后进后出。 栈VS队列-小结 3-3、数组 3-…

朗迪锋亮相2024人因工程与智能系统交互国际会议

2024年11月28日至30日&#xff0c;2024人因工程与智能系统交互国际会议在深圳隆重举办。此次大会以推动我国人因工程学科发展为目标&#xff0c;致力于加强国际学术交流&#xff0c;深入探讨人工智能时代的智能系统交互&#xff0c;旨在培育新质生产力&#xff0c;助力经济社会…

Java基础之控制语句:开启编程逻辑之门

一、Java控制语句概述 Java 中的控制语句主要分为选择结构、循环结构和跳转语句三大类&#xff0c;它们在程序中起着至关重要的作用&#xff0c;能够决定程序的执行流程。 选择结构用于根据不同的条件执行不同的代码路径&#xff0c;主要包括 if 语句和 switch 语句。if 语句有…

如何部署vue项目到Github Pages

1.创建vue项目 npm create vitelatest my-vue-app -- --template vue 2.创建github仓库 3.连接仓库 在项目根目录右键选择open git base here&#xff0c;如果没有安装git请先安装git。 初始化仓库 $ git init $ git add . $ git commit -m "init"将项目与仓库连…

Jenkins升级到最新版本后无法启动

1. 场景还原 最近在web界面将jenkins升级到最新版本后&#xff0c;后台无法启动jenkins服务&#xff0c;服务状态如下&#xff1a; 运行jenkins命令提示invalid Java version jenkins --version jenkins: invalid Java version: java version "1.8.0_202" Java(TM)…

「计算机网络性能指标」

速率 速率&#xff08;Speed&#xff09;&#xff1a;指连接到网络上的节点在信道上的传输数据的速率。也称数据率或比特率&#xff0c;数据传输速率 信道&#xff08;Channel&#xff09;&#xff1a;表示向某一方向传送信息的通道&#xff08;信道 ≠ 通信线路&#xff09;…

HTTP协议详解:从HTTP/1.0到HTTP/3的演变与优化

深入浅出&#xff1a;从头到尾全面解析HTTP协议 一、HTTP协议概述 1.1 HTTP协议简介 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是互联网上应用最广泛的通信协议之一。它用于客户端与服务器之间的数据传输&#xff0c;尤其是在Web…