Skip to content

Instantly share code, notes, and snippets.

@gterzian
Last active February 21, 2025 12:03
Show Gist options
  • Save gterzian/26d07e24d7fc59f5c713ecff35d68f01 to your computer and use it in GitHub Desktop.
Save gterzian/26d07e24d7fc59f5c713ecff35d68f01 to your computer and use it in GitHub Desktop.
How to use Copilot

How to work on a Web API with the assistance of Copilot

Part 1: Basic Setup of a Web API

  1. Read-up on the relevant spec.
  2. Add .webidl file(s) in this folder for each interface that you want to implement. If one already exists, you want to add the missing parts to it.
  3. For each interface, this will generate a trait named {interface_name}Methods, accessible via use crate::dom::bindings::codegen::Bindings::{interface_name}Binding.
  4. Use this trait by:
    • Adding a matching struct, using #[dom_struct]
    • Adding methods with todo! bodies.
  5. At this point, the struct can have only one member: reflector_: Reflector,
    • The struct should be documented with a link to its interface in the spec,
    • The trait methods should each be documented with a link to their definition in the interface.
    • Example result.
  6. Back in the spec,
    • look for definitions of futher members for your struct.
    • These are usually referred to as "internal slots"(example), or as something that is "associated" with the interface(example).
  7. Read-up on DOM interfaces.
  8. Using what you know, for each internal slot of the interface:
    • Add an appropriate member to the struct(s) added at 4.
    • If this requires defining other structs or enums, these should derive JSTraceable and MallocSizeOf(example).
    • Any JSTraceable structs added above that contains members that must be rooted because they are either JS values or Dom Objects should be marked with #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]. Example, where the lint is needed due to the presence of a JSVal.
    • If such a struct is assigned to a variable, impl js::gc::Rootable for the and use rooted! to root the variabe(example).
    • All of this can be changed later, so simply use your best judgement at this stage.
    • Add methods for construction(not to be confused with a Constructor that is part of the Web API).
    • Example result.

Part 2: Writing a First Draft with Copilot

  1. For each methods of the bindings trait referred to at 3 in Part 1:
    • In general, follow the structure of the spec: if a method calls into another named algorithm, implement that named algorithm as a separate private method of your struct, that the trait methods calls into. If you later realize this private method can be used from other structs, make it pub(crate).
    • For each algorithm step in the spec:
      • Copy the prose from the spec, but ommit the step number(these change too often).
      • Paste the prose into the code.
      • Here is where Copilot comes in: review the suggestion, and accept it if correct.
      • At irregular but frequent interval: ./mach check your code, and otherwise review it for correctness.
      • If Copilot suggest a non-existent method call, this may in fact be a good name for a named algorithm to be implemented: review the suggestion and implement the method if appropriate(perhaps just with a todo! for now).
      • Be wary of multi-line suggestions: these can be correct, and even include subsequent steps from the specification, all correctly documented. But it can also include non-sense. One line at a time is more reliable. When defining a new method, Copilot can suggest a useful signature, and sometimes even the entire body, but it is best to accept everyhting but then delete the body and re-do it one line at the time.
      • Copilot is good for boilerplate that often comes-up, such as the need to react to the fulfillment or rejection of a promise. This will require multiple lines of code and the definition of additional structs, which is again best done by acception suggestions one line at a time(or sometimes one method or struct signature at a time).
      • In order to protect against copyright infringement, please set the suggestions matching public code setting to Blocked.
    • Example result
  2. Note: there are certain things that are often needed to perform operation as part of an algorithm:
    • SafeJSContext: can be obtained using GlobalScope::get_cx().
    • GlobalScope: can be obtained using self.global() on a dom_struct, or GlobalScope::from_safe_context
    • InRealm: can be obtained as an argument to the generated trait method, using this configuration file
    • CanGc: same as above
    • It is best to access them as early as possible, say at the top of the trait method implementation, and to pass them down(as ref for GlobalScope) as arguments, in the order described above(with any other needed argument coming in between &GlobalScope and InRealm).
  3. This should give you a complete first draft.

Part 3: running tests and fixing bugs

  1. Now comes the time to identify which WPT test to run against your first draft. You can find them here.
  2. Test may fail because:
    • There is a bug in the code. These should be fixed.
    • The test uses other APIs that aren't supported yet(usually ERROR).
  3. Bugs should be fixed. Copilot is of little help here.
  4. Expected failures can be marked as such, using the process described here.
  5. This part is done when there are no unexpected test results left.
  6. On occasion, on the advice of a reviewer, you may file an issue and describe a failure that you cannot fix, mark the test as a failure, and leave it to a follow-up.

Part 4: refactoring and asking for a final review

  1. While you may ask for a review at any time if stuck, now is the time to take a last look at your code and decide if you want to refactor anything.
  2. Copilot is also useful at this stage:
    • You can use the chat to ask it to perform a refactoring.
    • The resulting edits should be reviewed and accepted one at a time.
  3. If you are satisfied, now is the time to ask for a final review.
  4. Congratulations, the new Web API you implemented should soon merge.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment