Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: new /doc command with smart selection #1116

Merged
merged 13 commits into from
Sep 22, 2023
Merged

feat: new /doc command with smart selection #1116

merged 13 commits into from
Sep 22, 2023

Conversation

abeatrix
Copy link
Contributor

@abeatrix abeatrix commented Sep 19, 2023

feat: add smart selection and update doc prompt

This PR focuses on updating the /doc command, featuring snart selection.

  • Add getActiveTextEditorSmartSelection method to editor interface for supporting "smart selection" on files that have lang server running as we rely on that to extract the ranges for each function / methods within a class
  • Update prompt for the /doc command to specify docstring should not contain code but docstring only
  • Introduce CommandRunner class that allows different services to run different commands async
  • Add mode field to cody commands config
  • Add insertMode and autoApply fields to FixupTask that allows fixup to auto apply suggestions returned by Cody instead of displaying Code Lenses
  • /doc command now uses fixup insert mode

What is smart selection?

Smart selection removes the need to manually highlight code before running the /doc and /test commands. Instead, it will automatically detect and run the selected command for the function nearest to your cursor position. For /doc, the docstring will be added to the top of the function through fixup.

Why?

Based on user testing conducted by Megan, many first-time command users were unaware of the requirement to highlight code before running commands. We can create a smoother onboarding experience for new users with smart selection. Allowing our commands to work seamlessly without manual code selection will lower the learning curve and provide a more intuitive first-time user experience.

However, it's important to note that smart selection should be treated as a fallback method, as it produces a selection based on guessing user intent. Manual selection truly reflects the user's real intent and should be used when possible. Moreover, smart selection may not work well for all languages, and depends on if users have language servers running in their editor. In cases where smart selection is unreliable, users should fall back to using the manual selection method to ensure accuracy.

Follow-up

  • improve /doc prompt
  • apply fixup automatically
  • indentation issues caused by fixup

Test plan

Update 2 - latest

autoapply-doc.mov

Update 1

smartselection.mov
  1. Start Cody from this branch
  2. Run /doc command in a function without selecting any code (but put your cursor inside a function)
  3. Run /doc command with code selected
  4. Repeat step 2 and 3 with /test command
  5. Run other commands, and use chat -- they should work as expected

@abeatrix abeatrix changed the title feat: add smart selection and update /doc command feat: new /doc command with smart selection Sep 19, 2023
@abeatrix abeatrix marked this pull request as ready for review September 20, 2023 06:45
@abeatrix abeatrix requested review from taras-yemets and a team September 20, 2023 06:55
Copy link
Contributor

@toolmantim toolmantim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so great @abeatrix! I love we're triggering a fixup, and just modifying the code!

Indention is often incorrect when documenting indented code… maybe something we can improve in the future with @valerybugakov's tree-sitter tech? One thing we could try in the future is auto-applying too. But one step at a time! :)

Is it possible to adjust the prompt so that Cody doesn't describe "parameters" in places where it doesn't make sense, because I'm getting this when documenting some selected code:

Screenshot 2023-09-20 at 5 10 21 pm

I gave it a go locally with this diff:

diff --git a/lib/shared/src/chat/prompts/default-prompts.json b/lib/shared/src/chat/prompts/default-prompts.json
index f1852861..def4714b 100644
--- a/lib/shared/src/chat/prompts/default-prompts.json
+++ b/lib/shared/src/chat/prompts/default-prompts.json
@@ -2,7 +2,7 @@
   "commands": {
     "doc": {
       "description": "Generate code documentation",
-      "prompt": "Generate a comment documenting the parameters and functionality for the selected code. Only generate the documentation for the selected code, do not generate the code. Use the same documentation style in the file of the selected code to generate the comments. Pay attention to the file path of the selected code to make sure the comments are generated for the correct language. For example, use the JavaDoc documentation style to generate comments for .java files, or Python docstring using Python multi-line string for .py files. Do not include any other code or comments besides the docstring.",
+      "prompt": "Generate a comment briefly documenting the purpose of the selected code. If it takes parameters, document those. Only generate the documentation for the selected code, do not generate the code. Use the same comment style in the file of the selected code, or the most common style for that language. If no existing documentation comments exist, pay attention to the file path of the selected code to make sure the comments are generated in the style of that language. Do not include any other code or comments besides the comment.",
       "context": {
         "currentDir": true,
         "selection": true

And ended up with better results for the selected lines:

Screenshot 2023-09-20 at 5 20 24 pm

That appears to still document functions correctly too:

Screenshot 2023-09-20 at 5 22 04 pm

I don't have a good test suite / cases to verify it across more examples, so will defer to your prompt magic skills!

"context": {
"currentDir": true,
"selection": true
}
},
"mode": "insert"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Does this mean custom commands can do this too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YES! Also takes "inline" as value where the prompt will be run in inline chat instead 😀

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow. This is ace!

@abeatrix
Copy link
Contributor Author

abeatrix commented Sep 20, 2023

The current one is the one from the old recipe so I didn't really do any prompt engineering on this, but yours looks good to me! @toolmantim

edit: we can make the changes to the prompt in a different PR

@abeatrix abeatrix requested a review from a team September 20, 2023 16:00
Copy link
Contributor

@philipp-spiess philipp-spiess left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome stuff! I didn't review everything but got curious how the smart selection works so I left some comments on that 👀

vscode/src/editor/utils.ts Outdated Show resolved Hide resolved
const symbols = await getSymbolRanges(uri).then(r => r.filter(s => s.kind === vscode.SymbolKind.Class))
// Get the folding ranges for the document,
const ranges = await getFoldingRange(uri).then(r => r.filter(r => r.kind !== 2 && r.kind !== 1 && r.kind !== 3))
// remove ranges that match the symbols from ranges
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why? It's not immediatly clear to me

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IT's coz if there is a function inside a class then the whole class is taken rather than the function alone.

1 | class OuterClass {
2 |     // ...
3 |     function something {
4 |         // ... cursor
5 |     }
6 |     // ...
7 | }

You wanna get line 3-5 instead of 1-7. But you get 1-7 unless you exclude folding ranges that match class symbol.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What @arafatkatze said! I also just added a comment to the function to detail my thought process, here is what I added:

The purpose of filtering to keep only folding ranges that contain other folding ranges is to find the outermost folding range enclosing the cursor position.

Folding ranges can be nested - you may have a folding range for a function that contains folding ranges for inner code blocks.

By filtering to ranges that contain other ranges, it removes the inner nested ranges and keeps only the outermost parent ranges.

This way when it checks for the range containing the cursor, it will return the outer range that fully encloses the cursor location, rather than an inner range that may only partially cover the cursor line.

However, if we have kept the ranges for classes, this may only return ranges for classes that contain individual methods rather than the outermost range of the methods within a class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OH this is only class symbols! This wasn't clear to me when I read it. So basically you just want to get all folding ranges that aren't classes, that makes sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But can't nesting of folding ranges happen regardless? Like if you have a function inside a function:

function createFoo() {
  return {
    bar() { ....},
    baz() { ....},
  }
}

Maybe we should find nesting and prefer the leaves?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, it looks like the prompt diff from #1116 (review) wasn't included in the end @abeatrix?

I didn't make any changes to the prompt in that PR since that was for the /doc behavior change.

Do you want to create a new PR with your change for the prompt?

Copy link
Contributor Author

@abeatrix abeatrix Sep 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@toolmantim Phillips was referring to the changes suggested by @arafatkatze i believe. We are only adding docstring for function scoops (with Smart Selection)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abeatrix ah! I don't think I've a good enough test suite / cases to verify prompt changes across more examples… I might leave that to you, unless we have a process for it I can follow.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after seeing it with the action, I wonder if we shouldn't make it so that only function folding ranges are considered for these. Especially when the action is "document code" since I would expect docstrings to be added for functions only

I agree with this given that just making doc strings for functions makes the most sense. IT would be still be nice to have some guardrails see this video, so I was able to send 10K tokens for my case and I can prolly send more if I want.
image.

WE could probably just have something that says like function context too large for LLM etc for the edge case of very large function. But yeah focussing on the function alone makes perfect sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philipp-spiess sorry for the ping but I wasn't sure if this got lost in the sea of comments.

@arafatkatze
Copy link
Contributor

arafatkatze commented Sep 20, 2023

@abeatrix This is truly amazing. Such a genius solution to this perplexing problem.

Sometimes the lines of the symbols ranges and the folding ranges don't match exactly and therefore the symbol range isn't removed.

            if (r.start === symbolRange.start.line && r.end === symbolRange.end.line) {
                ranges.splice(i, 1)
                i--
            }

This check is missed sometimes.

IF you try inside the class in /Users/arafatkhan/Desktop/cody/vscode/src/completions/providers/unstable-fireworks.ts and /Users/arafatkhan/Desktop/cody/vscode/src/completions/providers/unstable-openai.ts you will see that the folding range is missed so the functions inside the class take up the whole class instead of the function alone. Some function to do "close enough" match instead of exact match would help a lot here.

@abeatrix
Copy link
Contributor Author

abeatrix commented Sep 20, 2023

@toolmantim Updated the PR to fix the Indention issues you were seeing + added autoApply mode to fixup 🎉

autoapply-doc.mov

@arafatkatze thanks for your detailed feedback and for testing out the changes! smartSelection, unfortunately only works for files where users have lang servers running. If not, I think we should just fallback to using the manual selection for now, but if you have any suggestions on how we can extract ranges for functions in those cases please let us know. We can also look into this as a follow-up PR too (happy to collab!) 😄

@toolmantim
Copy link
Contributor

@abeatrix just tested this, and it works great! I tested undo/redo as well, and seems to be fine. The indentation it inserts is perfect now too.

All it needs is that prompt update and I think this is ready to rock! 🙌🏼🙌🏼🙌🏼

@@ -1,6 +1,9 @@
import * as defaultPrompts from './default-prompts.json'
import { toSlashCommand } from './utils'

// A list of default cody commands
export const defaultCodyCommands = ['ask', 'doc', 'edit', 'smell', 'test', 'reset']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: Not used anywhere

@toolmantim
Copy link
Contributor

Do we have a way of doing any e2e testing of this (perhaps with a few diff example languages/repos/scenarios)?

@abeatrix
Copy link
Contributor Author

Do we have a way of doing any e2e testing of this (perhaps with a few diff example languages/repos/scenarios)?

Hmmm it's using fixup which is already covered by an e2e test.

Is there something you have in mind that you want to make sure it's covered by a test particularly?

@toolmantim
Copy link
Contributor

Sounds like we don’t have this yet, but was just checking if there was a more automated way to run this command with diff scenarios of repos, files and selection… and verify the quality of output and behavior. My testing is still fairly adhoc.

Co-authored-by: Arafat  <arafat.da.khan@gmail.com>
@csells
Copy link

csells commented Sep 21, 2023

is there a way to keep the new docs commenting consistent with the rest of the doc comments in the file, e.g. // vs /* */

@abeatrix
Copy link
Contributor Author

is there a way to keep the new docs commenting consistent with the rest of the doc comments in the file, e.g. // vs /* */

Yes, that requires some prompt engineering, which we will work on next in another PR! 😄

@abeatrix abeatrix requested a review from a team September 22, 2023 02:05
@abeatrix
Copy link
Contributor Author

abeatrix commented Sep 22, 2023

@valerybugakov @dominiccooney @philipp-spiess @umpox can I have one of you review this PR when you have the time, please? Multiple people have tested what's in the change already (see conversations above), but will need someone from the eng side to do the final review/approval to move this forward, thank you 🙇‍♀️

Copy link
Contributor

@philipp-spiess philipp-spiess left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good stuff

Comment on lines +77 to +78
* However, if we keep the ranges for classes, this will then only return ranges for classes that contain individual methods rather
* than the outermost range of the methods within a class. So the first step is to remove class ranges.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not specific to classes btw. In some languages, it might be giant objects or modules. also in JS you could have a constructor function returning an object.

What about we define a target LOC count and while traversing upwards, we filter out ranges that are too large. Would that make sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about we define a target LOC count and while traversing upwards, we filter out ranges that are too large. Would that make sense?

Yea it does that's smarter too and removes the dependency of the user having code intel installed for the language! I can work on refining this in a follow-up PR, thanks for the suggestions!

Does graph context already have something similar in place I can refer to?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No my implementation is much worse and only looks at the document sections (so for classes it's currently useless). I was hoping I can just use your implementation after that 🙈

for (const cRange of classRanges) {
for (let i = 0; i < foldingRanges.length; i++) {
const r = foldingRanges[i]
if (Math.abs(r.start - cRange.start.line) <= 1 && Math.abs(r.end - cRange.end.line) <= 1) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoying that folding ranges are not normal ranges and thus have no contains() 🙃

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philipp-spiess omg right?!!! That tricked Cody autocompletion too 🤣

@abeatrix abeatrix merged commit 40316a7 into main Sep 22, 2023
11 checks passed
@abeatrix abeatrix deleted the bee/new-doc branch September 22, 2023 14:05
abeatrix added a commit that referenced this pull request Sep 22, 2023
This PR adds the missing Changelog items for [pull/1116
1139](#1116) &
#1139

Also fixed a minor oversight from pull/1116.

## Test plan

<!-- Required. See
https://docs.sourcegraph.com/dev/background-information/testing_principles.
-->

changelog update
toolmantim added a commit that referenced this pull request Sep 27, 2023
This updates the default prompt definitions from JSON to a regular TS
file, so we can better see changes in diffs & review suggestions (e.g.
#1116 (review)),
and add more documentation for why certain prompt sentences exist.

Part of #1178 

Side note: custom commands don't get to enjoy this luxury, but we could
add `Array` support for the `prompt` key at least.

## Test plan

- Ran each command and verified they still worked
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants