-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
lsp: Implement code actions for new fixes
#653 added fixes for no-whitespace-comment and use-assignment-operator. This PR adds code actions for these in the regal lsp. Signed-off-by: Charlie Egan <[email protected]>
- Loading branch information
1 parent
5a8c5c1
commit 3684090
Showing
9 changed files
with
350 additions
and
144 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package commands | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"strconv" | ||
|
||
"github.com/open-policy-agent/opa/ast" | ||
|
||
"github.com/styrainc/regal/internal/lsp/types" | ||
) | ||
|
||
type ParseOptions struct { | ||
TargetArgIndex int | ||
RowArgIndex int | ||
ColArgIndex int | ||
} | ||
|
||
type ParseResult struct { | ||
Target string | ||
Location *ast.Location | ||
} | ||
|
||
// Parse is responsible for extracting the target and location from the given params command params sent from the client | ||
// after acting on a Code Action. | ||
func Parse(params types.ExecuteCommandParams, opts ParseOptions) (*ParseResult, error) { | ||
if len(params.Arguments) == 0 { | ||
return nil, errors.New("no args supplied") | ||
} | ||
|
||
target := "" | ||
|
||
if opts.TargetArgIndex < len(params.Arguments) { | ||
target = fmt.Sprintf("%s", params.Arguments[opts.TargetArgIndex]) | ||
} | ||
|
||
// we can't extract a location from the same location as the target, so location arg positions | ||
// must not have been set in the opts. | ||
if opts.RowArgIndex == opts.TargetArgIndex { | ||
return &ParseResult{ | ||
Target: target, | ||
}, nil | ||
} | ||
|
||
var loc *ast.Location | ||
|
||
if opts.RowArgIndex < len(params.Arguments) && opts.ColArgIndex < len(params.Arguments) { | ||
var row, col int | ||
|
||
switch v := params.Arguments[opts.RowArgIndex].(type) { | ||
case int: | ||
row = v | ||
case string: | ||
var err error | ||
|
||
row, err = strconv.Atoi(v) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to parse row: %w", err) | ||
} | ||
default: | ||
return nil, fmt.Errorf("unexpected type for row: %T", params.Arguments[opts.RowArgIndex]) | ||
} | ||
|
||
switch v := params.Arguments[opts.ColArgIndex].(type) { | ||
case int: | ||
col = v | ||
case string: | ||
var err error | ||
|
||
col, err = strconv.Atoi(v) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to parse col: %w", err) | ||
} | ||
default: | ||
return nil, fmt.Errorf("unexpected type for col: %T", params.Arguments[opts.ColArgIndex]) | ||
} | ||
|
||
loc = &ast.Location{ | ||
Row: row, | ||
Col: col, | ||
} | ||
} | ||
|
||
return &ParseResult{ | ||
Target: target, | ||
Location: loc, | ||
}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package commands | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/open-policy-agent/opa/ast" | ||
|
||
"github.com/styrainc/regal/internal/lsp/types" | ||
) | ||
|
||
func TestParse(t *testing.T) { | ||
t.Parallel() | ||
|
||
testCases := map[string]struct { | ||
ExecuteCommandParams types.ExecuteCommandParams | ||
ParseOptions ParseOptions | ||
ExpectedTarget string | ||
ExpectedLocation *ast.Location | ||
}{ | ||
"extract target only": { | ||
ExecuteCommandParams: types.ExecuteCommandParams{ | ||
Command: "example", | ||
Arguments: []interface{}{"target"}, | ||
}, | ||
ParseOptions: ParseOptions{TargetArgIndex: 0}, | ||
ExpectedTarget: "target", | ||
ExpectedLocation: nil, | ||
}, | ||
"extract target and location": { | ||
ExecuteCommandParams: types.ExecuteCommandParams{ | ||
Command: "example", | ||
Arguments: []interface{}{"target", "1", 2}, // different types for testing, but should be strings | ||
}, | ||
ParseOptions: ParseOptions{TargetArgIndex: 0, RowArgIndex: 1, ColArgIndex: 2}, | ||
ExpectedTarget: "target", | ||
ExpectedLocation: &ast.Location{Row: 1, Col: 2}, | ||
}, | ||
} | ||
|
||
for name, tc := range testCases { | ||
tc := tc | ||
|
||
t.Run(name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
result, err := Parse(tc.ExecuteCommandParams, tc.ParseOptions) | ||
if err != nil { | ||
t.Fatalf("unexpected error: %v", err) | ||
} | ||
|
||
if result.Target != tc.ExpectedTarget { | ||
t.Fatalf("expected target %q, got %q", tc.ExpectedTarget, result.Target) | ||
} | ||
|
||
if tc.ExpectedLocation == nil && result.Location != nil { | ||
t.Fatalf("expected location to be nil, got %v", result.Location) | ||
} | ||
|
||
if tc.ExpectedLocation != nil { | ||
if result.Location == nil { | ||
t.Fatalf("expected location to be %v, got nil", tc.ExpectedLocation) | ||
} | ||
|
||
if result.Location.Row != tc.ExpectedLocation.Row { | ||
t.Fatalf("expected row %d, got %d", tc.ExpectedLocation.Row, result.Location.Row) | ||
} | ||
|
||
if result.Location.Col != tc.ExpectedLocation.Col { | ||
t.Fatalf("expected col %d, got %d", tc.ExpectedLocation.Col, result.Location.Col) | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestParse_Errors(t *testing.T) { | ||
t.Parallel() | ||
|
||
testCases := map[string]struct { | ||
ExecuteCommandParams types.ExecuteCommandParams | ||
ParseOptions ParseOptions | ||
ExpectedError string | ||
}{ | ||
"error extracting target": { | ||
ExecuteCommandParams: types.ExecuteCommandParams{ | ||
Command: "example", | ||
Arguments: []interface{}{}, // empty and so nothing can be extracted | ||
}, | ||
ParseOptions: ParseOptions{TargetArgIndex: 0}, | ||
ExpectedError: "no args supplied", | ||
}, | ||
} | ||
|
||
for name, tc := range testCases { | ||
tc := tc | ||
|
||
t.Run(name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
_, err := Parse(tc.ExecuteCommandParams, tc.ParseOptions) | ||
if err == nil { | ||
t.Fatalf("expected error %q, got nil", tc.ExpectedError) | ||
} | ||
|
||
if err.Error() != tc.ExpectedError { | ||
t.Fatalf("expected error %q, got %q", tc.ExpectedError, err.Error()) | ||
} | ||
}) | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.