Visual Studio:移动行的快捷键和浏览最近更改的快捷键

40

我正在从Eclipse转移到Visual Studio .NET,并发现所有我喜欢的快捷键,除了两个:

  • 在Eclipse中,您可以按ALT-ALT-来访问最近进行的更改,这是我经常使用的功能,可以返回到其他文件中的某个位置,然后返回。显然,在VS.NET中,CTRL--CTRL-SHIFT--也可以做到这一点,但它们似乎不总是起作用(例如在笔记本电脑上可能存在数字键问题)并且似乎不遵循与Eclipse相同的算法“我所在的位置”。有人成功使用过这个功能吗?是否每天都要依赖它等等?
  • 在Eclipse中,要将行向上或向下移动,您可以按ALT-uparrowALT-downarrow,并且您可以通过代码将其移动到所需位置,非常方便。另外,要复制一行,您可以按SHIFT-ALT-uparrowSHIFT-ALT-downarrow。这两个快捷键甚至适用于您选择的行块。

有人在Visual Studio .NET中发现过这些热键吗?

补充:

将底部行向上移动到for循环中的示例。在Eclipse中,您会将光标放在Console.WriteLine上,然后按ALT-(uparrow),我经常使用它:一次按键即可将行向上或向下移动。

for (int i = 0; i < 10; i++) {

}
Console.WriteLine(i);

好的,将Charlie的无选中控制C扩展到选择一行的想法,你可以在Visual Studio上将光标放在Console.WriteLine上(没有选择),按下CTRL-X,然后向上移动并按下CTRL-V


抱歉,我一开始误解了行移动的事情,但现在我想我明白了。请检查编辑后的宏以执行此操作。 - Charlie
9个回答

49

提出的答案都可以工作,但是它们没有Eclipse那样好,因为它们无法像Eclipse一样保留现有的粘贴缓冲区和当前选定的字符,并且它们不允许用户操作一系列行。这里是我想出的解决方案,它保留了粘贴缓冲区和当前字符选择,并且可以在有或没有选择的情况下使用(可能跨越多行):

'' Duplicates the current line (or selection of lines) and places the copy
'' one line below or above the current cursor position (based upon the parameter)
Sub CopyLine(ByVal movingDown As Boolean)
    DTE.UndoContext.Open("CopyLine")
    Dim objSel As TextSelection = DTE.ActiveDocument.Selection

    ' store the original selection and cursor position
    Dim topPoint As TextPoint = objSel.TopPoint
    Dim bottomPoint As TextPoint = objSel.BottomPoint
    Dim lTopLine As Long = topPoint.Line
    Dim lTopColumn As Long = topPoint.LineCharOffset
    Dim lBottomLine As Long = bottomPoint.Line
    Dim lBottomColumn As Long = bottomPoint.LineCharOffset()

    ' copy each line from the top line to the bottom line
    Dim readLine As Long = lTopLine
    Dim endLine As Long = lBottomLine + 1
    Dim selectionPresent As Boolean = ((lTopLine <> lBottomLine) Or (lTopColumn <> lBottomColumn))
    If (selectionPresent And (lBottomColumn = 1)) Then
        ' A selection is present, but the cursor is in front of the first character
        ' on the bottom line. exclude that bottom line from the copy selection.
        endLine = lBottomLine
    End If

    ' figure out how many lines we are copying, so we can re-position
    ' our selection after the copy is done
    Dim verticalOffset As Integer = 0
    If (movingDown) Then
        verticalOffset = endLine - lTopLine
    End If

    ' copy each line, one at a time.
    ' The Insert command doesn't handle multiple lines well, and we need
    ' to use Insert to avoid autocompletions
    Dim insertLine As Long = endLine
    While (readLine < endLine)
        ' move to read postion, and read the current line
        objSel.MoveToLineAndOffset(readLine, 1)
        objSel.EndOfLine(True) 'extend to EOL
        Dim lineTxt As String = objSel.Text.Clone
        ' move to the destination position, and insert the copy
        objSel.MoveToLineAndOffset(insertLine, 1)
        objSel.Insert(lineTxt)
        objSel.NewLine()
        ' adjust the read & insertion points
        readLine = readLine + 1
        insertLine = insertLine + 1
    End While

    ' restore the cursor to original position and selection
    objSel.MoveToLineAndOffset(lBottomLine + verticalOffset, lBottomColumn)
    objSel.MoveToLineAndOffset(lTopLine + verticalOffset, lTopColumn, True)
    DTE.UndoContext.Close()
End Sub

'' Duplicates the current line (or selection of lines) and places the copy
'' one line below the current cursor position
Sub CopyLineDown()
    CopyLine(True)
End Sub

'' Duplicates the current line (or selection of lines) and places the copy
'' one line above the current cursor position
Sub CopyLineUp()
    CopyLine(False)
End Sub


'' Moves the selected lines up one line. If no line is
'' selected, the current line is moved.
''
Sub MoveLineUp()
    DTE.UndoContext.Open("MoveLineUp")
    Dim objSel As TextSelection = DTE.ActiveDocument.Selection
    ' store the original selection and cursor position
    Dim topPoint As TextPoint = objSel.TopPoint
    Dim bottomPoint As TextPoint = objSel.BottomPoint
    Dim lTopLine As Long = topPoint.Line
    Dim lTopColumn As Long = topPoint.LineCharOffset
    Dim lBottomLine As Long = bottomPoint.Line
    Dim lBottomColumn As Long = bottomPoint.LineCharOffset()

    Dim textLineAbove As TextSelection = DTE.ActiveDocument.Selection
    textLineAbove.MoveToLineAndOffset(lTopLine - 1, 1, False)
    textLineAbove.MoveToLineAndOffset(lTopLine, 1, True)
    Dim indentChange As Integer = CountIndentations(textLineAbove.Text) * -1

    ' If multiple lines are selected, but the bottom line doesn't
    ' have any characters selected, don't count it as selected
    Dim lEffectiveBottomLine = lBottomLine
    If ((lBottomColumn = 1) And (lBottomLine <> lTopLine)) Then
        lEffectiveBottomLine = lBottomLine - 1
    End If

    ' move to the line above the top line
    objSel.MoveToLineAndOffset(lTopLine - 1, 1)
    ' and move it down, until its below the bottom line:
    Do
        DTE.ExecuteCommand("Edit.LineTranspose")
    Loop Until (objSel.BottomPoint.Line >= lEffectiveBottomLine)
    ' Since the line we are on has moved up, our location in the file has changed:
    lTopLine = lTopLine - 1
    lBottomLine = lBottomLine - 1

    IndentBlockAndRestoreSelection(objSel, lBottomLine, lBottomColumn, lTopLine, lTopColumn, indentChange)

    DTE.UndoContext.Close()
End Sub

'' Moves the selected lines down one line. If no line is
'' selected, the current line is moved.
''
Sub MoveLineDown()
    DTE.UndoContext.Open("MoveLineDown")
    Dim objSel As TextSelection = DTE.ActiveDocument.Selection
    ' store the original selection and cursor position
    Dim topPoint As TextPoint = objSel.TopPoint
    Dim bottomPoint As TextPoint = objSel.BottomPoint
    Dim lTopLine As Long = topPoint.Line
    Dim lTopColumn As Long = topPoint.LineCharOffset
    Dim lBottomLine As Long = bottomPoint.Line
    Dim lBottomColumn As Long = bottomPoint.LineCharOffset()

    ' If multiple lines are selected, but the bottom line doesn't
    ' have any characters selected, don't count it as selected
    Dim lEffectiveBottomLine = lBottomLine
    If ((lBottomColumn = 1) And (lBottomLine <> lTopLine)) Then
        lEffectiveBottomLine = lBottomLine - 1
    End If

    Dim textLineBelow As TextSelection = DTE.ActiveDocument.Selection
    textLineBelow.MoveToLineAndOffset(lEffectiveBottomLine + 1, 1, False)
    textLineBelow.MoveToLineAndOffset(lEffectiveBottomLine + 2, 1, True)
    Dim indentChange As Integer = CountIndentations(textLineBelow.Text)


    ' move to the bottom line
    objSel.MoveToLineAndOffset(lEffectiveBottomLine, 1)
    ' and move it down, which effectively moves the line below it up
    ' then move the cursor up, always staying one line above the line
    ' that is moving up, and keep moving it up until its above the top line:
    Dim lineCount As Long = lEffectiveBottomLine - lTopLine
    Do
        DTE.ExecuteCommand("Edit.LineTranspose")
        objSel.LineUp(False, 2)
        lineCount = lineCount - 1
    Loop Until (lineCount < 0)
    ' Since the line we are on has moved down, our location in the file has changed:
    lTopLine = lTopLine + 1
    lBottomLine = lBottomLine + 1

    IndentBlockAndRestoreSelection(objSel, lBottomLine, lBottomColumn, lTopLine, lTopColumn, indentChange)

    DTE.UndoContext.Close()
End Sub

'' This method takes care of indenting the selected text by the indentChange parameter
'' It then restores the selection to the lTopLine:lTopColumn - lBottomLine:lBottomColumn parameter.
'' It will adjust these values according to the indentChange performed
Sub IndentBlockAndRestoreSelection(ByVal objSel As TextSelection, ByVal lBottomLine As Long, ByVal lBottomColumn As Long, ByVal lTopLine As Long, ByVal lTopColumn As Long, ByVal indentChange As Integer)
    ' restore the cursor to original position and selection
    objSel.MoveToLineAndOffset(lBottomLine, lBottomColumn)
    objSel.MoveToLineAndOffset(lTopLine, lTopColumn, True)
    If (indentChange = 0) Then
        ' If we don't change the indent, we are done
        Return
    End If

    If (lBottomLine = lTopLine) Then
        If (indentChange > 0) Then
            objSel.StartOfLine()
        Else
            objSel.StartOfLine()
            objSel.WordRight()
        End If
    End If
    objSel.Indent(indentChange)

    ' Since the selected text has changed column, adjust the columns accordingly:
    ' restore the cursor to original position and selection
    Dim lNewBottomColumn As Long = (lBottomColumn + indentChange)
    Dim lNewTopColumn As Long = (lTopColumn + indentChange)
    ' ensure that we we still on the page.
    ' The "or" clause makes it so if we were at the left edge of the line, we remain on the left edge.
    If ((lNewBottomColumn < 2) Or (lBottomColumn = 1)) Then
        ' Single line selections, or a bottomColumn that is already at 1 may still have a new BottomColumn of 1
        If ((lTopLine = lBottomLine) Or (lBottomColumn = 1)) Then
            lNewBottomColumn = 1
        Else
            ' If we have multiple lines selected, don't allow the bottom edge to touch the left column,
            ' or the next move will ignore that bottom line.
            lNewBottomColumn = 2
        End If
    End If
    If ((lNewTopColumn < 2) Or (lTopColumn = 1)) Then
        lNewTopColumn = 1
    End If

    ' restore the selection to the modified selection
    objSel.MoveToLineAndOffset(lBottomLine, lNewBottomColumn)
    objSel.MoveToLineAndOffset(lTopLine, lNewTopColumn, True)
End Sub


'' This method counts the indentation changes within the text provided as the paramter
Function CountIndentations(ByVal text As String) As Integer
    Dim indent As Integer = 0
    While (Text.Length > 0)
        If (Text.StartsWith("//")) Then
            Dim endOfLine As Integer = Text.IndexOf("\n", 2)
            If (Equals(endOfLine, -1)) Then
                ' The remaining text is all on one line, so the '//' terminates our search
                ' Ignore the rest of the text
                Exit While
            End If
            ' continue looking after the end of line
            Text = Text.Substring(endOfLine + 1)
        End If

        If (Text.StartsWith("/*")) Then
            Dim endComment As Integer = Text.IndexOf("*/", 2)
            If (Equals(endComment, -1)) Then
                ' This comment continues beyond the length of this line.
                ' Ignore the rest of the text
                Exit While
            End If
            ' continue looking after the end of this comment block
            Text = Text.Substring(endComment + 1)
        End If

        If (Text.StartsWith("{")) Then
            indent = indent + 1
        Else
            If (Text.StartsWith("}")) Then
                indent = indent - 1
            End If
        End If
        Text = Text.Substring(1)
    End While
    Return indent
End Function

我编辑了这篇文章,在MoveLineUp()和MoveLineDown()方法的开头添加了UndoContext机制(由Nicolas Dorier建议),并在它们的结尾处关闭了它。

11/23/11 - 我再次更新了这个功能,使移动的行可以在跨越括号边界时自动缩进。


太棒了。我希望我可以投票超过一次。干得好! - Matt Blaine
4
这应该是答案,而不是Mitch Wheat的那个。干得好! - Borek Bernard
实际上,当我“懒惰”地选择行时,即在第一行的中间某处开始选择,并在最后一行的中间某处结束选择时,这个脚本就不起作用了。 Eclipse可以完美处理这个问题。 - Borek Bernard
“懒惰式”选择对我很有效。唯一让人烦恼的是微软的宏通知弹出,真的应该有一种方法可以关闭它。 - ocodo
Borek,我修改了CopyLine函数以处理多行选择。之前它不正确,但现在试试看。 - Paul Ostrowski
7
太棒了!这个方法非常有效!对于不熟悉宏和害怕VB的人:只需打开宏浏览器(工具|宏|宏浏览器),添加一个新的宏项目,将上面的代码复制并粘贴到宏模块文件中。然后在“键盘快捷键设置”(工具|选项|环境|键盘)中搜索MoveLineUp命令(或宏浏览器中显示的任何其他命令)。找到宏之后,只需分配所需的快捷键(确保它们不与其他键冲突)。 - Tommy Bravo

16

1
这似乎是迄今为止最简单的解决方案,但公平地说 - 我想在这个解决方案出现之前就有人问过这个问题了。 - javamonkey79

14

如果你还没有找到它,设置这些键盘快捷方式的地方在“工具”|“选项”|“环境”|“键盘”下。很多有用的命令可以通过浏览列表找到,尽管不幸的是我从来没有找到过任何好的参考资料来描述每个命令的作用。

至于具体的命令:

  • 我相信你所说的前进/后退导航命令是View.NavigateBackward和View.NavigateForward。如果你的键盘与VS键绑定不协调,你可以将它们重新映射到你喜欢的Eclipse键。不幸的是,我不知道如何更改它实际决定去哪里的算法。

  • 我不认为有一个内置的命令来复制一行,但是在没有选择文本时按Ctrl+C会将当前行复制到剪贴板上。鉴于此,这里是一个简单的宏,可以将当前行复制到下一行:


    Sub CopyLineBelow()
        DTE.ActiveDocument.Selection.Collapse()
        DTE.ActiveDocument.Selection.Copy()
        DTE.ActiveDocument.Selection.Paste()
    End Sub

    Sub CopyLineAbove()
        DTE.ActiveDocument.Selection.Collapse()
        DTE.ActiveDocument.Selection.Copy()
        DTE.ActiveDocument.Selection.LineUp()
        DTE.ActiveDocument.Selection.Paste()
    End Sub
  • 对于移动文本行,Edit.LineTranspose将选定的行向下移动。我认为没有向上移动行的命令,但这里有一个快速的宏来完成它:

    Sub MoveLineUp()
        DTE.ActiveDocument.Selection.Collapse()
        DTE.ActiveDocument.Selection.Cut()
        DTE.ActiveDocument.Selection.LineUp()
        DTE.ActiveDocument.Selection.Paste()
        DTE.ActiveDocument.Selection.LineUp()
    End Sub

如果你还没有开始使用宏,它们非常有用。 工具| 宏 | 宏IDE 会带您进入编辑器,一旦定义,您可以通过同样的UI设置键盘快捷方式,就像我上面提到的那样。 我使用极其方便的“记录临时宏”命令生成了这些宏,该命令也在“工具|宏”下。 此命令允许您记录一组键盘输入并重放它们任意次数,这对于构建高级编辑命令以及自动化重复任务(例如代码重新格式化)非常有用。


2
CTRL-C、CTRL-V 没有选择文本直接复制一行的功能很不错,我之前不知道这个,谢谢。 - Edward Tanguay

8

我最近做了同样的事情,当我开始一个新项目时,从Eclipse转移到了Visual Studio。强烈推荐使用Resharper插件 - 它为VS添加了一些丰富的编辑、导航和重构功能,就像eclipse一样。

Resharper还允许您使用与InteliJ非常相似的键盘映射方案,这对于Java逃民非常方便...

关于您的第二个问题,Resharper具有与eclipse相同的向上/向下移动代码的功能,但有一些注意事项。首先,使用InteliJ键盘映射,组合键相当复杂。

移动代码向上:ctrl + shift + alt + 上箭头

移动代码向下:ctrl + shift + alt + 下箭头

其次,它不总是只移动一行,而实际上跳过代码块。所以它不能将一行从if语句外部移动到其中 - 它会直接跳过if块选择的行。要执行此操作,您需要使用

将代码移入外部代码块:ctrl + shift + alt + 左箭头

将代码移入下一个内部代码块:ctrl + shift + alt + 右箭头


2

编辑。LineTranspose不能将行向上移动...这是将行向上移动的宏:

Sub LineTransposeUp()
    Dim offset As Integer
    Dim sel As TextSelection

    DTE.UndoContext.Open("LineTransposeUp")

    Try
        sel = DTE.ActiveDocument.Selection
        offset = sel.ActivePoint.LineCharOffset
        sel.LineUp()
        DTE.ExecuteCommand("Edit.LineTranspose")
        sel.LineUp()
        sel.MoveToLineAndOffset(sel.ActivePoint.Line, offset)
    Catch ex As System.Exception
    End Try

    DTE.UndoContext.Close()
End Sub

2

在Visual Studio中记录宏来执行alt-arrow操作:

ctrl-alt-r -- record mode
ctrl-c -- copy a line
up arrow -- go up a line
home -- beginning of line (maybe there is a way to paste before the current line without this)
ctrl-v -- paste
ctrl-alt-r -- end record mode

现在,您可以使用宏ide和键盘首选项将该宏映射到任何一组您喜欢的按键。

1
使用MoveLine扩展在VS 2010/2012中将一行(或一组行)向上或向下移动。

0

我不知道VS是否原生支持你所说的功能,但我知道resharper插件允许你使用CTRL + SHIFT + BACKSPACE快捷键跳转到之前的编辑位置。不过我还没有发现它支持上下移动行的功能。


它确实有上下移动行的快捷键,但它的功能与Eclipse中的不完全相同 - 请参阅我在下面的回答。 - serg10

0

Paul Ostrowski,我试用了你的工具。它基本上运行良好。

Eclipse 的另一项功能是将行移动到当前缩进级别。

例如:

function test()
{
    // do stuff
}
Console.WriteLine("test"); 

在控制台输出中进行一次shift-up操作会将其更改为

function test()
{
    // do stuff
    Console.WriteLine("test"); 
}

但是你的工具似乎可以做到这一点:

function test()
{
    // do stuff
Console.WriteLine("test"); 
}

我已经将这个功能添加到我的解决方案中。它不是100%完美,但覆盖了大多数使用情况。 - Paul Ostrowski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接