Autohotkey将在执行期间过早结束循环

时间:2015-04-29 11:31:19

标签: sql-server sql-server-2012 autohotkey

我编写了一个AutoHotkey脚本,该脚本允许我在Microsoft SQL Server Management Studio 2012中光标的当前位置运行SQL查询。它可以工作 - 大部分时间都是如此。

该实现使用一种增量移动来确定它何时到达SQL查询块的顶部或窗口的顶部。然后向下选择,直到它以相同的方式到达窗口的底部或块的底部。之后,按F5键运行突出显示的脚本。

这是脚本:

$F5::
    ; first check that current line isn't blank (the cursor has to be on some text)
    Send, {Home}
    hltext := SelectNextChar()
    If hltext = `r`n
    {
        ; return to current position
        Send, {Left}
        Return
    }

    ; move cursor up until it gets to the top of the text block

    ; get first length for future comparisons
    Send, {End}
    Send, {Shift Down}{Home}{Shift Up}
    Send, ^c
    StringLen, slctSize, clipboard

    ; begin checking lengths to see if there is still 'movement'
    Loop
    {
        Send, {Shift Down}{Left}{Home}{Shift Up}
        Send, ^c

        ; if the new length is the same then we've hit the top and can break out
        StringLen, temp, clipboard
        IfEqual, slctSize, %temp%
        {
            Send, {Left}
            Break
        }

        ; if we have hit a blank space then we can stop here as well
        firstChar := SubStr(clipboard, 1, 1)
        If firstChar is space
        {
            Send, {Left}{Down}
            Break
        }

        ; if neither one of these conditions are met, continue on
        slctSize = %temp%
        Sleep, 50
    }

    ; select down until blank space or end of file

    Send, {Shift Down}{End}{Shift Up}
    Send, ^c
    StringLen, slctSize, clipboard

    Loop
    {
        Send, {Shift Down}{Right}{End}{Shift Up}
        Send, ^c

        StringLen, temp, clipboard
        IfEqual, slctSize, %temp%
        {
            Break
        }

        ; if we have hit a blank space then we can stop here as well
        lastChar := SubStr(clipboard, 0)
        If lastChar is space
        {
            Break
        }

        ; if neither one of these conditions are met, continue on
        slctSize = %temp%
        Sleep, 50
    }

    ; execute!
    Send, {F5}
    ; place cursor at end of last line
    Send, {Right}
Return


SelectNextChar()
{
    Send, {LShift Down}{Right}{LShift Up}
    Send, ^c
    return %clipboard%
}

这些动作 - 向上然后向下 - 有时会过早结束,导致在按下F5时不能完全选择查询。对于小型查询,这不是问题;对于跨越大约10行的大型查询,很快就会发生一些事情。

我已经测试了将Sleep, 500置于循环中,并且似乎来执行此操作,但此脚本的重点是在运行中进行测试查询快得多。如果我等待超过2或3秒钟(希望)突出显示,那么是什么让它比我的旧策略更快,即用鼠标手动突出显示?

要明确的是,较大的查询 大部分都在运行,但过程速度不足以保证使用,并且无法保证在执行前它们会完全突出显示。我也明白我的实现本质上是O(x ^ 2)。但是,如果我在完全爆炸(SetKeyDelay, -1)上运行脚本,那不会有问题。

有关此事的任何想法或仅仅是操作系统/程序/ AHK的限制?

另外,它只是我还是SO的语法突出显示严重破坏?

更新:这是一个包含建议修改的更新脚本:

SendMode, Input ; Very fast but gives unpredictable results at low sleep speeds
SetBatchLines, -1

$F5::
    ; first check that current line isn't blank (the cursor has to be on some text)
    Send, {Home}
    hltext := SelectNextChar()
    If hltext = `r`n
    {
        ; return to current position
        Send, {Left}
        Return
    }

    ; move cursor up until it gets to the top of the text block

    ; get first length for future comparisons
    Send, {End}
    Send, {Shift Down}{Home}{Shift Up}
    Send, ^c
    SleepAfterCopy()
    StringLen, slctSize, clipboard

    ; begin checking lengths to see if there is still 'movement'
    Loop
    {
        Send, {Shift Down}{Left}{Home}{Shift Up}
        Send, ^c
        SleepAfterCopy()

        ; if the new length is the same then we've hit the top and can break out
        StringLen, temp, clipboard
        IfEqual, slctSize, %temp%
        {
            Send, {Left}
            Break
        }

        ; if we have hit a blank space then we can stop here as well
        firstChar := SubStr(clipboard, 1, 1)
        If firstChar is space
        {
            Send, {Left}{Down}
            Break
        }

        ; if neither one of these conditions are met, continue on
        slctSize = %temp%
    }

    ; select down until blank space or end of file

    Send, {Shift Down}{End}{Shift Up}
    Send, ^c
    SleepAfterCopy()
    StringLen, slctSize, clipboard

    Loop
    {
        Send, {Shift Down}{Right}{End}{Shift Up}
        Send, ^c
        SleepAfterCopy()

        StringLen, temp, clipboard
        IfEqual, slctSize, %temp%
        {
            Break
        }

        ; if we have hit a blank space then we can stop here as well
        lastChar := SubStr(clipboard, 0)
        If lastChar is space
        {
            Break
        }

        ; if neither one of these conditions are met, continue on
        slctSize = %temp%
    }

    ; execute!
    Send, {F5}
    ; place cursor at end of last line
    Send, {Right}
Return


SelectNextChar()
{
    Send, {LShift Down}{Right}{LShift Up}
    Send, ^c
    return %clipboard%
}

SleepAfterCopy()
{
    Sleep, 50
}

更新2:以下是@Sidola中包含ClipWait的版本。因此它尽可能快地运行。此更新还包括一些逻辑,以确保如果您事先复制了某些内容,则由于我们过度使用剪贴板而无法将其删除。最后,它考虑了行的开头/结尾处的任何缩进或空格:

SendMode, Input ; Very fast but gives unpredictable results at low sleep speeds
SetBatchLines, -1

#IfWinActive ahk_exe Ssms.exe

$F5::
    ; Save current clipboard material and restore it at the end
    before = %clipboard%

    ; first check that current line isn't blank (the cursor has to be on some text)
    Send, {Home}
    clipboard =
    hltext := SelectNextChar()
    If hltext = `r`n
    {
        ; return to current position
        Send, {Left}
        Return
    }

    ; move cursor up until it gets to the top of the text block

    ; get first length for future comparisons
    Send, {End}
    Send, {Shift Down}{Home}{Home}{Shift Up}
    clipboard =
    Send, ^c
    SleepAfterCopy()
    StringLen, slctSize, clipboard

    ; begin checking lengths to see if there is still 'movement'
    Loop
    {
        Send, {Shift Down}{Left}{Home}{Home}{Shift Up}
        clipboard =
        Send, ^c
        SleepAfterCopy()

        ; if the new length is the same then we've hit the top and can break out
        StringLen, temp, clipboard
        IfEqual, slctSize, %temp%
        {
            Send, {Left}
            Break
        }

        ; if we have hit a blank space then we can stop here as well
        firstChar := SubStr(clipboard, 1, 1)
        If firstChar = `r
        {
            Send, {Left}{Down}
            Break
        }



        ; if neither one of these conditions are met, continue on
        slctSize = %temp%
    }

    ; select down until blank space or end of file

    Send, {Shift Down}{End}{Shift Up}
    clipboard =
    Send, ^c
    SleepAfterCopy()
    StringLen, slctSize, clipboard

    Loop
    {
        Send, {Shift Down}{Right}{End}{Shift Up}
        clipboard =
        Send, ^c
        SleepAfterCopy()

        StringLen, temp, clipboard
        IfEqual, slctSize, %temp%
        {
            Break
        }

        ; if we have hit a blank space then we can stop here as well
        lastChar := SubStr(clipboard, 0)
        If lastChar = `n
        {
            Break
        }

        ; if neither one of these conditions are met, continue on
        slctSize = %temp%
    }

    ; execute!
    Send, {F5}
    ; place cursor at end of last line
    Send, {Right}

    ; restore clipboard
    clipboard = %before%
Return

+F5::
    Send, {F5}
Return


SelectNextChar()
{
    Send, {LShift Down}{Right}{LShift Up}
    Send, ^c
    SleepAfterCopy()
    return %clipboard%
}

SleepAfterCopy()
{
    ClipWait
    ; Sleep, 30
}

1 个答案:

答案 0 :(得分:1)

它过早结束的原因可能是由于copy-command在你开始工作之前没有足够的时间来正确更新剪贴板。

处理此问题的最佳方法是在复制任何内容之前清除剪贴板,并依赖ClipWait告诉我们何时复制了某些内容。或者让它超时告诉我们没有任何内容被复制。

ClipWait还允许我们检测文档的顶部和底部而不检查重复项,因为我们只是等待超时。

以下是一个尽可能快的工作示例。

但请注意:此脚本仅适用于copy-command正常运行的程序。这意味着如果您没有选择进行复制,则不会复制任在尝试这个时,我发现有些程序没有这种行为,因此这个脚本不适用于那些程序。在这些情况下,您将不得不采用某种方式检查重复项。

SendMode, Input
SetBatchLines, -1

Esc::ExitApp

$F5::
    ; Check if the line we're currently at is just a line break
    if (isLineBreak( getFirstChar() ))
        return

    ; Get to the top of the document
    traverseText("up")
    ; Get to the bottom
    lineCount := traverseText("down")
    ; Select everything
    selectAllLines(lineCount)
return

; --- Only functions below ---

selectAllLines(lineCount) {
    Send, {LShift Down}
    Loop, % lineCount {
        Send, {Up}
    }
    Send, {Home}
    Send, {LShift Up}
} 

traverseText(direction) {
    Loop {
        selectLine(direction)
        thisLine := copyText()

        ; If it's just a line break, we're out
        if (isLineBreak(thisLine)) {

            ; If we were going up we want to move down once first
            if (direction = "up")
                Send, {Down}

            break
        }

        ; If nothing was copied, we're out
        if (!thisLine)
            break

        i := A_Index ; Keep track of how many lines we've moved passed
    }

    ; If we had a line break beneath us
    ; we need to add one to the counter
    if (thisLine)
        i++

    return i ; Return the amount of lines we traversed
}

isLineBreak(value) {
    return value = "`r`n"
}

getFirstChar() {
    Send, {Home}+{Down} ; Home, Shift + Down
    char := copyText()
    Send, {Left}
    return char
}

selectLine(direction) {
    Send, % direction = "up" ? "{Home}" : "{End}" ; Ternary operator 
    Send, {LShift Down}
    Send, % direction = "up" ? "{Up}" : "{Down}" ; Ternary operator 
    Send, {LShift Up}
}

copyText() {
    Clipboard := ""
    Send, ^c
    ClipWait, 0.2 ; Time-out after 200ms
    return Clipboard
}