【问题标题】:Tab and Shift-Tab smart indent on selected text选定文本上的 Tab 和 Shift-Tab 智能缩进
【发布时间】:2012-05-08 19:15:37
【问题描述】:

在大多数现代编辑器中,您可以突出显示一段代码并缩进或取消缩进制表符或您正在使用的许多空格,这是标准的;你如何在 emacs 中做到这一点?

所以,比如我刚刚打开了 sublime text,高亮了下面这段代码:

variation1 person phoneMap carrierMap addressMap =
    case M.lookup person phoneMap of
      Nothing -> Nothing
      Just number ->
          case M.lookup number carrierMap of
            Nothing -> Nothing
            Just carrier -> M.lookup carrier addressMap

然后按tab键得到

  variation1 person phoneMap carrierMap addressMap =
      case M.lookup person phoneMap of
        Nothing -> Nothing
        Just number ->
            case M.lookup number carrierMap of
              Nothing -> Nothing
              Just carrier -> M.lookup carrier addressMap

该代码上的一个 shift-tab 会将其返回到原来的位置,如果我继续按 shift-tab,我最终会得到以下信息:

variation1 person phoneMap carrierMap addressMap =
case M.lookup person phoneMap of
Nothing -> Nothing
Just number ->
case M.lookup number carrierMap of
Nothing -> Nothing
Just carrier -> M.lookup carrier addressMap

引用另一个回复:

emacs 语言模式并没有“缩进这个块”的概念 1 个标签进一步'。相反,他们非常固执己见,并且有一个概念 “这是正确的缩进”,这就是你点击时得到的 语言模式下的选项卡。

除非我使用以下代码执行此操作(haskell 模式和 ghc mod 已启用):

import Monad
import System
import IO
import Random
import Control.Monad.State

type RandomState a = State StdGen a
data CountedRandom = CountedRandom {
      crGen :: StdGen
    , crCount :: Int
    }

type CRState = State CountedRandom

getRandom :: Random a => RandomState a
getRandom =
  get >>= \gen ->
  let (val, gen') = random gen in
  put gen' >>
  return val  

我得到以下信息:

import Monad
  import System
  import IO
  import Random
  import Control.Monad.State

type RandomState a = State StdGen a
data CountedRandom = CountedRandom {
  crGen :: StdGen
  , crCount :: Int
  }

type CRState = State CountedRandom

               getRandom :: Random a => RandomState a
               getRandom =
  get >>= \gen ->
  let (val, gen') = random gen in
  put gen' >>
  return val  

当我想要的时候

import Monad
import System
import IO
import Random
import Control.Monad.State

type RandomState a = State StdGen a
data CountedRandom = CountedRandom {
      crGen :: StdGen
    , crCount :: Int
    }

type CRState = State CountedRandom

getRandom :: Random a => RandomState a
getRandom =
  get >>= \gen ->
    let (val, gen') = random gen in
    put gen' >>
    return val  

足够接近 ataylor 的解决方案:

(defcustom tab-shift-width 4
  "Sets selected text shift width on tab"
  :type 'integer)
(make-variable-buffer-local 'tab-shift-width)

(global-set-key 
 (kbd "<tab>")
 (lambda (start end)
   (interactive "r")
   (if (use-region-p)
       (save-excursion
     (let ((deactivate-mark nil))
       (indent-rigidly start end tab-shift-width)))
     (indent-for-tab-command))))

(global-set-key 
 (kbd "S-<tab>")
 (lambda (start end)
   (interactive "r")
   (if (use-region-p)
       (save-excursion
     (let ((deactivate-mark nil))
       (indent-rigidly start end (- tab-shift-width))))
     (indent-for-tab-command))))

如果 emacs 支持缩进检测就更好了(即,只获取某个变量的值);我发现最接近这个的是一个名为 dtrt indent 的插件,但它不适用于 Haskell。

【问题讨论】:

  • 不知道emacs,但在vim中有一个矩形选择模式;在矩形选择模式下插入文本会将该文本插入每一行的相同位置。如果 emacs 有一个类似的东西(它可能有),这样的事情应该可以解决你的问题。

标签: haskell emacs


【解决方案1】:

indent-region 将根据当前模式重新缩进一段文本。

要强制添加缩进级别,您可以使用string-rectangle,它会提示您输入字符串。在这里,您可以提供缩进级别的字符串(例如制表符、4 个空格等)。该字符串将插入当前选定区域的每一行,在当前列中,有效地缩进它。或者,您可以从open-rectangle 获得类似的效果,它将在矩形中插入空白,其角由点和标记定义。

另一种强制缩进的方法是调用indent-rigidly (C-x TAB)。这会覆盖特定于模式的缩进规则并缩进固定数量。数字参数指定缩进多少,负参数将取消缩进。如果您希望这是选择区域时的默认行为,您可以执行以下操作:

(global-set-key 
 (kbd "<tab>")
 (lambda (start end)
   (interactive "r")
   (if (use-region-p)
       (save-excursion
     (let ((deactivate-mark nil))
       (indent-rigidly start end 4)))
     (indent-for-tab-command))))

【讨论】:

  • 如何将其绑定到 shift-tab/tab 以便它读取我正在使用的缩进类型并应用所需的行为?我是否必须学习 elisp 才能添加大多数其他编辑器中给出的标准行为?
  • 更新了我的答案。 indent-rigidly 几乎可以满足您的需求。添加了一个示例,说明如何在选择区域时将其绑定到 TAB
【解决方案2】:

Haskell 代码很难正确缩进,因为一段代码有多个“正确”缩进。

haskell-mode 有一个非常具体的行格式,它希望您遵循(例如,您必须在正确的位置进行换行),并且它有一些缩进规则用于格式化与该行格式匹配的代码。存在这些规则是为了使自动缩进结果更加一致。规则大致是这样的:

  • 在每个引入块的关键字之后,您应该换行或确保整个块适合布局。否则,你会得到很多像 getRandom 示例中的悬挂块
  • 所有块都缩进两个空格。这包括模块块;如果您执行module Bla where,则该行之后的整个部分将缩进。这意味着您应该保留默认的 Haskell 模块文件格式,以便制定缩进顺序。
  • 行的缩进需要尽可能明确;如果一行可能根据其缩进表示不同的含义,它将导致它缩进到haskell-mode 认为在上下文中有意义的位置。在某些情况下可能无法解决此问题。

因为不可能将 Haskell 代码构造成满足haskell-mode 的要求,所以你不能像这样缩进一段时间的 Haskell 代码文件。您只需要在本地使用自动缩进。这可以通过多种方式完成:

  • 当您在一行中时,您可以通过按 TAB 将当前行缩进到相对于前一行而言最可能“正确”的位置。通过再次按 TAB,您会将行带到“下一个”缩进级别,并不断循环所有可能的逻辑缩进步骤。
  • 如果您选择在本地找到的一系列块(函数体等)并使用 M-x indent-region,则结果很可能是正确的。

在这种情况下,我通常会从有“错误”缩进的行开始,按一次 TAB,然后逐行向下,按 每行 TAB 一次或多次,直到该行的缩进正确。当前行的当前“逻辑缩进位置”是根据前面的代码上下文计算的,因此从顶部更正缩进几乎总是会产生正确的结果。

【讨论】:

    【解决方案3】:

    我突出显示该区域并点击C-M-\。这是indent-region,更多乐趣可以在multi-line indent页面找到。

    【讨论】:

    • 当我对我的代码示例执行此操作 (C-M-\) 时,它看起来就像我的问题中的最后一个示例(缺少所有选项卡)。当我使用 indent-code-rigidly 时,它只会将它向前移动一个空格。真的,我想找到一个绑定或者可能是一个 emacs 插件,它为我提供了我曾经使用过的所有其他代码编辑器的 tab/shift-tab 行为。例如,在 vim 中我会做vmap &lt;TAB&gt; &gt;gv vmap &lt;S-TAB&gt; &lt;gv。如何在 emacs 中做相当于 that 的操作?
    • 那是什么语言的代码?从我在您的代码中看到的 sn-p 只是移动了 2 个空格,不是吗?因此缩进区域将基于“它应该在哪里”进行缩进,并将此 sn-p 上方的线作为基线。也许这种语言在它的模式下有它自己的缩进区域?
    • 这是haskell,我在尝试您的建议时清除了我的.emacs。我刚刚用 C++ 代码尝试了它,它自动将选择缩进到它认为应该是的位置(这是错误的)。
    • 啊,在我的回答中,我假设您使用的是haskell-mode,去那里获取它:github.com/haskell/haskell-mode
    • 是的,我两种方式都试过了。并且在不同的语言中:没有给出期望的行为。
    【解决方案4】:

    emacs 语言模式实际上并没有“进一步缩进此块 1 选项卡”的概念。相反,他们非常固执己见,并且有“这是正确的缩进”的概念,这就是您在语言模式下点击制表符时所得到的。

    一旦你习惯了它,其他任何事情都会显得很奇怪。在您的 Haskell 代码中,这些 case 语句实际上只有一个有效的缩进,除非您添加大括号和分号,否则其他任何内容都是语法错误。如果您真的想自定义 emacs 认为“正确”缩进的内容,请查看如何自定义您的模式。很多语言模式重用 c-mode 变量,所以here 可能是一个不错的起点(虽然我不确定 Haskell 模式是做什么的,但我从未发现需要自定义它)。

    编辑: 我在您的评论中看到您的问题是没有安装 Haskell 模式,请到 github page 获取它。

    【讨论】:

      猜你喜欢
      • 2013-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      • 2010-10-23
      • 1970-01-01
      • 2011-09-22
      相关资源
      最近更新 更多