diff --git a/data/playground/go/values.go b/data/playground/go/values.go new file mode 100644 index 0000000000..8e0257c2ce --- /dev/null +++ b/data/playground/go/values.go @@ -0,0 +1,55 @@ +package p + +func returnZero() { + return +} + +func returnOne() string { + return "lorem" +} + +func returnOneWithCommentBefore() string { + return /* comment */ "lorem" +} + +func returnOneWithCommentAfter() string { + return "lorem" /* comment */ +} + + +func returnTwo() (string, string) { + return "lorem", "ipsum" +} + +func returnTwoWithCommentInside() (string, string) { + return "lorem" /* comment */, "ipsum" +} + +func returnTwoWithCommentBefore() (string, string) { + return /* comment */ "lorem", "ipsum" +} + +func returnTwoWithCommentAfter() (string, string) { + return "lorem", "ipsum" /* comment */ +} + + +func returnThree() (string, string, string) { + return "lorem", "ipsum", "blah" +} + +func returnThreeWithCommentInside() (string, string, string) { + return "lorem" /* comment */, "ipsum", "blah" +} + +func returnThreeWithCommentInside2() (string, string, string) { + return "lorem", "ipsum" /* comment */, "blah" +} + +func returnThreeWithCommentBefore() (string, string, string) { + return /* comment */ "lorem", "ipsum", "blah" +} + +func returnThreeWithCommentAfter() (string, string, string) { + return "lorem", "ipsum", "blah" /* comment */ +} diff --git a/packages/common/src/types/Position.ts b/packages/common/src/types/Position.ts index 79f4d9063b..e4b423218b 100644 --- a/packages/common/src/types/Position.ts +++ b/packages/common/src/types/Position.ts @@ -138,4 +138,16 @@ export class Position { public toEmptyRange(): Range { return new Range(this, this); } + + /** + * Return a concise string representation of the position. + * @returns concise representation + **/ + public concise(): string { + return `${this.line}:${this.character}`; + } + + public toString(): string { + return this.concise(); + } } diff --git a/packages/common/src/types/Range.ts b/packages/common/src/types/Range.ts index 55ea3b4029..b0ffe057f3 100644 --- a/packages/common/src/types/Range.ts +++ b/packages/common/src/types/Range.ts @@ -146,4 +146,16 @@ export class Range { ? new Selection(this.end, this.start) : new Selection(this.start, this.end); } + + /** + * Return a concise string representation of the range + * @returns concise representation + **/ + public concise(): string { + return `${this.start.concise()}-${this.end.concise()}`; + } + + public toString(): string { + return this.concise(); + } } diff --git a/packages/common/src/types/Selection.ts b/packages/common/src/types/Selection.ts index f54ac06787..a202c47251 100644 --- a/packages/common/src/types/Selection.ts +++ b/packages/common/src/types/Selection.ts @@ -72,4 +72,16 @@ export class Selection extends Range { this.anchor.isEqual(other.anchor) && this.active.isEqual(other.active) ); } + + /** + * Return a concise string representation of the selection + * @returns concise representation + **/ + public concise(): string { + return `${this.anchor.concise()}->${this.active.concise()}`; + } + + public toString(): string { + return this.concise(); + } } diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/checkCaptureStartEnd.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/checkCaptureStartEnd.ts index e9b13d22b2..d254f5bd04 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/checkCaptureStartEnd.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/checkCaptureStartEnd.ts @@ -60,7 +60,9 @@ export function checkCaptureStartEnd( showError( messages, "TreeSitterQuery.checkCaptures.mixRegularStartEnd", - `Please do not mix regular captures and start/end captures: ${captures}`, + `Please do not mix regular captures and start/end captures: ${captures.map( + ({ name, range }) => name + "@" + range.toString(), + )}`, ); shownError = true; } @@ -71,7 +73,7 @@ export function checkCaptureStartEnd( messages, "TreeSitterQuery.checkCaptures.duplicate", `A capture with the same name may only appear once in a single pattern: ${captures.map( - ({ name }) => name, + ({ name, range }) => name + "@" + range.toString(), )}`, ); shownError = true; diff --git a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts index e1647f5f1e..921a9ae469 100644 --- a/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts +++ b/packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts @@ -73,6 +73,22 @@ class HasMultipleChildrenOfType extends QueryPredicateOperator { + name = "has-multiple-children-not-of-type?" as const; + schema = z.tuple([q.node, q.string]); + + run({ node }: MutableQueryCapture, type: string) { + const count = node.children.filter((n) => n.type !== type).length; + return count > 1; + } +} + class ChildRange extends QueryPredicateOperator { name = "child-range!" as const; schema = z.union([ @@ -187,4 +203,5 @@ export const queryPredicateOperators = [ new AllowMultiple(), new InsertionDelimiter(), new HasMultipleChildrenOfType(), + new HasMultipleChildrenNotOfType(), ]; diff --git a/packages/cursorless-engine/src/languages/go.ts b/packages/cursorless-engine/src/languages/go.ts index 208d16e222..302747b8c1 100644 --- a/packages/cursorless-engine/src/languages/go.ts +++ b/packages/cursorless-engine/src/languages/go.ts @@ -27,11 +27,6 @@ const nodeMatchers: Partial< patternMatcher("parameter_declaration"), patternMatcher("argument_declaration"), ), - collectionKey: "keyed_element[0]", - value: cascadingMatcher( - patternMatcher("keyed_element[1]"), - patternMatcher("return_statement.expression_list!"), - ), }; export default createPatternMatchers(nodeMatchers); diff --git a/queries/go.scm b/queries/go.scm index 1f71bde6f5..e002793f17 100644 --- a/queries/go.scm +++ b/queries/go.scm @@ -222,3 +222,87 @@ . ) ) @anonymousFunction @namedFunction + +;; keys in maps +(literal_value + "{" @collectionKey.iteration.start.endOf + (keyed_element + (_) @collectionKey @collectionKey.trailing.start.endOf + ":" + (_) @collectionKey.trailing.end.startOf + ) @collectionKey.domain + "}" @collectionKey.iteration.end.startOf +) + +;; values in maps +(literal_value + "{" @value.iteration.start.endOf + (keyed_element + (_) @value.leading.start.endOf + ":" + (_) @value @value.leading.end.startOf + ) @value.domain + "}" @value.iteration.end.startOf +) + +;; values in return statements + +;; one value within a return statement +(return_statement + (expression_list + . + (_) + . + ) @value + (#insertion-delimiter! @value ", ") +) @value.domain @value.iteration + +;; multiple values within a return statement + +;; NB: gofmt puts the comma after block comments in lists of things +;; +;; Like this: +;; return "lorem" /* comment */, "ipsum" +;; Not like this: +;; return "lorem", /* comment */ "ipsum" +;; +;; It's really hard to deal with commas both before and after, +;; and they're rare anyway, so assume gofmt for now. +;; Non-gofmt commas will mess up removal ranges. + +;; the first value + +;; BUG: in this code: +;; return "lorem" /* comment */ , "ipsum" +;; the comment is included in the removal range of "lorem". +;; This is too hard to fix now, because it would require +;; disjoint removal ranges. And it is rare anyway. + +;; the first of many return values... +(return_statement + (expression_list + . + (_) @value @value.trailing.start.endOf + (#insertion-delimiter! @value ", ") + . + (comment)* @value.trailing.omit + . + "," + . + (_) @value.trailing.end.startOf + ) @_exprlist @value.leading.start.startOf + (#not-type? @value comment) + (#has-multiple-children-not-of-type? @_exprlist comment) +) @value.iteration + +;; ...and the rest of the values +(return_statement + (expression_list + "," @value.leading.start.startOf + . + (_) @value @value.leading.start.startOf + (#insertion-delimiter! @value ", ") + ) @_exprlist + (#not-type? @value comment) + (#has-multiple-children-not-of-type? @_exprlist comment) +) @value.iteration