mirror of
https://github.com/golang/go
synced 2024-11-18 10:54:40 -07:00
internal/lsp/source: support highlight of switch statements
Placing the cursor on a switch statement or corresponding break and call document.Highlight will now highlight them. Fixes golang/go#39275 Change-Id: Ib7e3ba0c6e78141ed3dd37cfd3b72b567b857247 Reviewed-on: https://go-review.googlesource.com/c/tools/+/238478 Run-TryBot: Daniel Martí <mvdan@mvdan.cc> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
parent
c7475b9d7f
commit
037be6a065
@ -90,6 +90,8 @@ func highlightPath(pkg Package, path []ast.Node) (map[posRange]struct{}, error)
|
|||||||
highlightIdentifiers(pkg, path, result)
|
highlightIdentifiers(pkg, path, result)
|
||||||
case *ast.ForStmt, *ast.RangeStmt:
|
case *ast.ForStmt, *ast.RangeStmt:
|
||||||
highlightLoopControlFlow(path, result)
|
highlightLoopControlFlow(path, result)
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
highlightSwitchFlow(path, result)
|
||||||
case *ast.BranchStmt:
|
case *ast.BranchStmt:
|
||||||
// BREAK can exit a loop, switch or select, while CONTINUE exit a loop so
|
// BREAK can exit a loop, switch or select, while CONTINUE exit a loop so
|
||||||
// these need to be handled separately. They can also be embedded in any
|
// these need to be handled separately. They can also be embedded in any
|
||||||
@ -235,8 +237,11 @@ func highlightUnlabeledBreakFlow(path []ast.Node, result map[posRange]struct{})
|
|||||||
case *ast.ForStmt, *ast.RangeStmt:
|
case *ast.ForStmt, *ast.RangeStmt:
|
||||||
highlightLoopControlFlow(path, result)
|
highlightLoopControlFlow(path, result)
|
||||||
return // only highlight the innermost statement
|
return // only highlight the innermost statement
|
||||||
case *ast.SelectStmt, *ast.SwitchStmt:
|
case *ast.SwitchStmt:
|
||||||
// TODO: add highlight when breaking a select or switch.
|
highlightSwitchFlow(path, result)
|
||||||
|
return
|
||||||
|
case *ast.SelectStmt:
|
||||||
|
// TODO: add highlight when breaking a select.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -254,19 +259,21 @@ func highlightLabeledFlow(node *ast.BranchStmt, result map[posRange]struct{}) {
|
|||||||
switch label.Stmt.(type) {
|
switch label.Stmt.(type) {
|
||||||
case *ast.ForStmt, *ast.RangeStmt:
|
case *ast.ForStmt, *ast.RangeStmt:
|
||||||
highlightLoopControlFlow([]ast.Node{label.Stmt, label}, result)
|
highlightLoopControlFlow([]ast.Node{label.Stmt, label}, result)
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
highlightSwitchFlow([]ast.Node{label.Stmt, label}, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func highlightLoopControlFlow(path []ast.Node, result map[posRange]struct{}) {
|
func labelFor(path []ast.Node) *ast.Ident {
|
||||||
labelFor := func(path []ast.Node) *ast.Ident {
|
if len(path) > 1 {
|
||||||
if len(path) > 1 {
|
if n, ok := path[1].(*ast.LabeledStmt); ok {
|
||||||
if n, ok := path[1].(*ast.LabeledStmt); ok {
|
return n.Label
|
||||||
return n.Label
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func highlightLoopControlFlow(path []ast.Node, result map[posRange]struct{}) {
|
||||||
var loop ast.Node
|
var loop ast.Node
|
||||||
var loopLabel *ast.Ident
|
var loopLabel *ast.Ident
|
||||||
stmtLabel := labelFor(path)
|
stmtLabel := labelFor(path)
|
||||||
@ -344,6 +351,74 @@ Outer:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func highlightSwitchFlow(path []ast.Node, result map[posRange]struct{}) {
|
||||||
|
var switchNode ast.Node
|
||||||
|
var switchNodeLabel *ast.Ident
|
||||||
|
stmtLabel := labelFor(path)
|
||||||
|
Outer:
|
||||||
|
// Reverse walk the path till we get to the switch statement.
|
||||||
|
for i := range path {
|
||||||
|
switch n := path[i].(type) {
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
switchNodeLabel = labelFor(path[i:])
|
||||||
|
if stmtLabel == nil || switchNodeLabel == stmtLabel {
|
||||||
|
switchNode = n
|
||||||
|
break Outer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Cursor is not in a switch statement
|
||||||
|
if switchNode == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the switch statement.
|
||||||
|
rng := posRange{
|
||||||
|
start: switchNode.Pos(),
|
||||||
|
end: switchNode.Pos() + token.Pos(len("switch")),
|
||||||
|
}
|
||||||
|
result[rng] = struct{}{}
|
||||||
|
|
||||||
|
// Traverse AST to find break statements within the same switch.
|
||||||
|
ast.Inspect(switchNode, func(n ast.Node) bool {
|
||||||
|
switch n.(type) {
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
return switchNode == n
|
||||||
|
case *ast.ForStmt, *ast.RangeStmt, *ast.SelectStmt:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
b, ok := n.(*ast.BranchStmt)
|
||||||
|
if !ok || b.Tok != token.BREAK {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Label == nil || labelDecl(b.Label) == switchNodeLabel {
|
||||||
|
result[posRange{start: b.Pos(), end: b.End()}] = struct{}{}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// We don't need to check other switches if we aren't looking for labeled statements.
|
||||||
|
if switchNodeLabel == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find labeled break statements in any switch
|
||||||
|
ast.Inspect(switchNode, func(n ast.Node) bool {
|
||||||
|
b, ok := n.(*ast.BranchStmt)
|
||||||
|
if !ok || b.Tok != token.BREAK {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.Label != nil && labelDecl(b.Label) == switchNodeLabel {
|
||||||
|
result[posRange{start: b.Pos(), end: b.End()}] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func labelDecl(n *ast.Ident) *ast.Ident {
|
func labelDecl(n *ast.Ident) *ast.Ident {
|
||||||
if n == nil {
|
if n == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -80,9 +80,9 @@ Outer:
|
|||||||
if i == 1 {
|
if i == 1 {
|
||||||
break Outer //@mark(brk6, "break Outer"),highlight(brk6, forDecl5, brk5, brk6, brk8)
|
break Outer //@mark(brk6, "break Outer"),highlight(brk6, forDecl5, brk5, brk6, brk8)
|
||||||
}
|
}
|
||||||
switch i { //@mark(switch1, "switch"),highlight(switch1)
|
switch i { //@mark(switch1, "switch"),highlight(switch1, switch1, brk7)
|
||||||
case 5:
|
case 5:
|
||||||
break //@mark(brk7, "break"),highlight(brk7)
|
break //@mark(brk7, "break"),highlight(brk7, switch1, brk7)
|
||||||
case 6:
|
case 6:
|
||||||
continue //@mark(cont5, "continue"),highlight(cont5, forDecl6, cont5)
|
continue //@mark(cont5, "continue"),highlight(cont5, forDecl6, cont5)
|
||||||
case 7:
|
case 7:
|
||||||
@ -92,6 +92,32 @@ Outer:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testSwitch() {
|
||||||
|
var i, j int
|
||||||
|
|
||||||
|
L1:
|
||||||
|
for { //@mark(forDecl7, "for"),highlight(forDecl7, forDecl7, brk10, cont6)
|
||||||
|
L2:
|
||||||
|
switch i { //@mark(switch2, "switch"),highlight(switch2, switch2, brk11, brk12, brk13)
|
||||||
|
case 1:
|
||||||
|
switch j { //@mark(switch3, "switch"),highlight(switch3, switch3, brk9)
|
||||||
|
case 1:
|
||||||
|
break //@mark(brk9, "break"),highlight(brk9, switch3, brk9)
|
||||||
|
case 2:
|
||||||
|
break L1 //@mark(brk10, "break L1"),highlight(brk10, forDecl7, brk10, cont6)
|
||||||
|
case 3:
|
||||||
|
break L2 //@mark(brk11, "break L2"),highlight(brk11, switch2, brk11, brk12, brk13)
|
||||||
|
default:
|
||||||
|
continue //@mark(cont6, "continue"),highlight(cont6, forDecl7, brk10, cont6)
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
break //@mark(brk12, "break"),highlight(brk12, switch2, brk11, brk12, brk13)
|
||||||
|
default:
|
||||||
|
break L2 //@mark(brk13, "break L2"),highlight(brk13, switch2, brk11, brk12, brk13)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testReturn() bool { //@mark(func1, "func"),mark(bool1, "bool"),highlight(func1, func1, fullRet11, fullRet12),highlight(bool1, bool1, false1, bool2, true1)
|
func testReturn() bool { //@mark(func1, "func"),mark(bool1, "bool"),highlight(func1, func1, fullRet11, fullRet12),highlight(bool1, bool1, false1, bool2, true1)
|
||||||
if 1 < 2 {
|
if 1 < 2 {
|
||||||
return false //@mark(ret11, "return"),mark(fullRet11, "return false"),mark(false1, "false"),highlight(ret11, func1, fullRet11, fullRet12)
|
return false //@mark(ret11, "return"),mark(fullRet11, "return false"),mark(false1, "false"),highlight(ret11, func1, fullRet11, fullRet12)
|
||||||
|
2
internal/lsp/testdata/lsp/summary.txt.golden
vendored
2
internal/lsp/testdata/lsp/summary.txt.golden
vendored
@ -14,7 +14,7 @@ ImportCount = 8
|
|||||||
SuggestedFixCount = 12
|
SuggestedFixCount = 12
|
||||||
DefinitionsCount = 53
|
DefinitionsCount = 53
|
||||||
TypeDefinitionsCount = 2
|
TypeDefinitionsCount = 2
|
||||||
HighlightsCount = 60
|
HighlightsCount = 69
|
||||||
ReferencesCount = 11
|
ReferencesCount = 11
|
||||||
RenamesCount = 25
|
RenamesCount = 25
|
||||||
PrepareRenamesCount = 7
|
PrepareRenamesCount = 7
|
||||||
|
Loading…
Reference in New Issue
Block a user