选定文本上的Tab键和Shift-Tab智能缩进

时间:2012-05-07 19:14:40

标签: haskell emacs

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

所以,例如我刚刚打开了sublime文本,突出显示了以下代码:

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不起作用。

4 个答案:

答案 0 :(得分:2)

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

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

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

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

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

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

答案 1 :(得分:1)

indent-region将根据当前模式重新加入一个文本块。

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

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

(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))))

答案 2 :(得分:0)

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

答案 3 :(得分:0)

emacs语言模式实际上没有“进一步缩进此块1选项卡”的概念。相反,他们非常自以为是,并且有一个“这是正确的缩进”的概念,这就是你在语言模式下点击标签时得到的结果。

一旦你习惯它,其他任何东西看起来都很奇怪。在你的Haskell代码中,这些case语句只有一个有效的缩进,除非你添加大括号和分号,否则其他任何东西都是语法错误。如果您真的想要自定义emacs认为“正确”缩进的内容,请查看如何自定义模式。许多语言模式重用c模式变量,因此here可能是一个很好的起点(尽管我不确定Haskell模式的作用,但我从未发现需要对其进行自定义)。

编辑: 我在你的评论中看到你的麻烦来自没有安装Haskell模式,到github page得到它。