phetsims / phet-info

Collection of information shared by PhET team members for the purpose of using github effectively and for other process-related topics.
MIT License
63 stars 27 forks source link

duplicate code analysis? #37

Closed pixelzoom closed 7 years ago

pixelzoom commented 7 years ago

https://github.com/phetsims/scenery-phet/issues/278 identified code related to "color profiles" that had been copy-pasted in at least 4 places.

In https://github.com/phetsims/scenery-phet/issues/278#issuecomment-259749038, @jonathanolson asked:

As a side-note, is there an easy way to analyze our code base for duplicated (copy-pasted) code?

I tried IDEA's Analyze->Locate Duplicates menu item, which has some (but not much) customization. To locate this type of duplication (between repositories) it was necessary to scan the entire project, and the results weren't particularly useful - mostly false positives, like looking for a needle in a haystack. I didn't see the ColorProfiles duplication identified, though it may have been in there somewhere. Or maybe I'm using this feature incorrectly.

pixelzoom commented 7 years ago

11/10/16 dev meeting notes: • We should try not to duplicate code. • When something sim-specific is going to be reused, the person who needs to reuse it should make the attempt to generalize and move to common code. • When something in common code needs to be enhanced, the person who identifies the enhancement should make the enhancement to common code. • Ideally enhancements should be identified in the design phase.

samreid commented 7 years ago

I'm interested in spending a little time to see if we can automate checks for duplicated code--this could run regularly on aqua or as part of build steps.

samreid commented 7 years ago

For instance, jscpd seems like it may be able to support our project.

samreid commented 7 years ago

I used this .cpd.yaml file in chipper:

#.cpd.yaml
languages:
  - javascript
files:
  - "../acid-base-solutions/js/**/*"
  - "../area-builder/js/**/*"
  - "../area-model-multiplication/js/**/*"
  - "../arithmetic/js/**/*"
  - "../atomic-interactions/js/**/*"
  - "../balancing-act/js/**/*"
  - "../balancing-chemical-equations/js/**/*"
  - "../balloons-and-static-electricity/js/**/*"
  - "../beers-law-lab/js/**/*"
  - "../bending-light/js/**/*"
  - "../blast/js/**/*"
  - "../build-a-molecule/js/**/*"
  - "../build-an-atom/js/**/*"
  - "../calculus-grapher/js/**/*"
  - "../capacitor-lab-basics/js/**/*"
  - "../chains/js/**/*"
  - "../charges-and-fields/js/**/*"
  - "../circuit-construction-kit-black-box-study/js/**/*"
  - "../color-vision/js/**/*"
  - "../concentration/js/**/*"
  - "../curve-fitting/js/**/*"
  - "../energy-forms-and-changes/js/**/*"
  - "../energy-skate-park-basics/js/**/*"
  - "../estimation/js/**/*"
  - "../example-sim/js/**/*"
  - "../expression-exchange/js/**/*"
  - "../faradays-law/js/**/*"
  - "../fluid-pressure-and-flow/js/**/*"
  - "../forces-and-motion-basics/js/**/*"
  - "../fraction-comparison/js/**/*"
  - "../fraction-matcher/js/**/*"
  - "../friction/js/**/*"
  - "../function-builder/js/**/*"
  - "../gene-expression-essentials/js/**/*"
  - "../graphing-lines/js/**/*"
  - "../graphing-quadratics/js/**/*"
  - "../gravity-and-orbits/js/**/*"
  - "../gravity-force-lab/js/**/*"
  - "../gravity-force-lab-basics/js/**/*"
  - "../griddle/js/**/*"
  - "../hookes-law/js/**/*"
  - "../isotopes-and-atomic-mass/js/**/*"
  - "../john-travoltage/js/**/*"
  - "../joist/js/**/*"
  - "../least-squares-regression/js/**/*"
  - "../make-a-ten/js/**/*"
  - "../masses-and-springs/js/**/*"
  - "../models-of-the-hydrogen-atom/js/**/*"
  - "../molarity/js/**/*"
  - "../molecule-polarity/js/**/*"
  - "../molecule-shapes/js/**/*"
  - "../molecule-shapes-basics/js/**/*"
  - "../molecules-and-light/js/**/*"
  - "../neuron/js/**/*"
  - "../ohms-law/js/**/*"
  - "../pendulum-lab/js/**/*"
  - "../ph-scale/js/**/*"
  - "../ph-scale-basics/js/**/*"
  - "../plinko-probability/js/**/*"
  - "../projectile-motion/js/**/*"
  - "../proportion-playground/js/**/*"
  - "../protein-synthesis/js/**/*"
  - "../reactants-products-and-leftovers/js/**/*"
  - "../resistance-in-a-wire/js/**/*"
  - "../rutherford-scattering/js/**/*"
  - "../scenery-phet/js/**/*"
  - "../seasons/js/**/*"
  - "../simula-rasa/js/**/*"
  - "../states-of-matter/js/**/*"
  - "../states-of-matter-basics/js/**/*"
  - "../sun/js/**/*"
  - "../trig-tour/js/**/*"
  - "../under-pressure/js/**/*"
  - "../unit-rates/js/**/*"
  - "../vegas/js/**/*"
  - "../vibe/js/**/*"
  - "../wave-on-a-string/js/**/*"
exclude:
  - "**/*.min.js"
  - "**/*.mm.js"
output: "jscpd-out.txt"
reporter: "json"

And it told me:

~/github/chipper$ ../node_modules/jscpd/bin/jscpd
info:    jscpd - copy/paste detector for programming source code, developed by Andrey Kucherenko
info:    Preprocessors running time: durationMs=1937
info:    Scanning 3343 files for duplicates...
info:    Scanning for duplicates time: durationMs=87775
info:    Scanning... done!

info:    Start report generation...

info:    Found 291 exact clones with 5775 duplicated lines in 318 files

    - /Users/samreid/github/acid-base-solutions/js/mysolution/view/ConcentrationSlider.js: 5-21
     /Users/samreid/github/acid-base-solutions/js/mysolution/view/StrengthSlider.js: 5-21

    - /Users/samreid/github/area-builder/js/game/model/AreaBuilderChallengeFactory.js: 138-156
     /Users/samreid/github/area-builder/js/game/model/AreaBuilderChallengeFactory.js: 163-181

    - /Users/samreid/github/area-builder/js/game/model/AreaBuilderChallengeFactory.js: 686-696
     /Users/samreid/github/area-builder/js/game/model/AreaBuilderChallengeFactory.js: 762-772

    - /Users/samreid/github/area-builder/js/game/view/GameIconFactory.js: 81-95
     /Users/samreid/github/area-builder/js/game/view/GameIconFactory.js: 123-137

    - /Users/samreid/github/area-builder/js/game/view/GameIconFactory.js: 115-128
     /Users/samreid/github/area-builder/js/game/view/GameIconFactory.js: 137-150

    - /Users/samreid/github/area-builder/js/area-builder-config.js: 10-43
     /Users/samreid/github/arithmetic/js/arithmetic-config.js: 11-44

    - /Users/samreid/github/arithmetic/js/divide/model/DivideModel.js: 105-117
     /Users/samreid/github/arithmetic/js/multiply/model/MultiplyModel.js: 84-96

    - /Users/samreid/github/balancing-act/js/balancelab/view/GirlCreatorNode.js: 14-33
     /Users/samreid/github/balancing-act/js/balancelab/view/ManCreatorNode.js: 14-33

    - /Users/samreid/github/balancing-act/js/balancelab/view/GirlCreatorNode.js: 14-33
     /Users/samreid/github/balancing-act/js/balancelab/view/WomanCreatorNode.js: 14-33

    - /Users/samreid/github/balancing-act/js/common/view/LevelSupportColumnNode.js: 29-68
     /Users/samreid/github/balancing-act/js/common/view/TiltedSupportColumnNode.js: 30-69

    - /Users/samreid/github/balancing-act/js/game/model/BalanceGameChallengeFactory.js: 514-522
     /Users/samreid/github/balancing-act/js/game/model/BalanceGameChallengeFactory.js: 541-549

    - /Users/samreid/github/area-builder/js/game/view/StartGameLevelNode.js: 14-27
     /Users/samreid/github/balancing-act/js/game/view/StartGameLevelNode.js: 12-25

    - /Users/samreid/github/area-builder/js/game/view/StartGameLevelNode.js: 59-84
     /Users/samreid/github/balancing-act/js/game/view/StartGameLevelNode.js: 55-80

    - /Users/samreid/github/area-builder/js/game/view/StartGameLevelNode.js: 104-113
     /Users/samreid/github/balancing-act/js/game/view/StartGameLevelNode.js: 94-103

    - /Users/samreid/github/area-builder/js/game/view/StartGameLevelNode.js: 112-129
     /Users/samreid/github/balancing-act/js/game/view/StartGameLevelNode.js: 102-119

    - /Users/samreid/github/balancing-chemical-equations/js/common/view/BalanceScalesNode.js: 47-61
     /Users/samreid/github/balancing-chemical-equations/js/common/view/BarChartsNode.js: 47-61

    - /Users/samreid/github/balancing-chemical-equations/js/common/view/BalanceScalesNode.js: 127-141
     /Users/samreid/github/balancing-chemical-equations/js/common/view/BarChartsNode.js: 134-148

    - /Users/samreid/github/balancing-chemical-equations/js/common/model/MoleculeFactory.js: 13-50
     /Users/samreid/github/balancing-chemical-equations/js/game/view/BCERewardNode.js: 18-55

    - /Users/samreid/github/acid-base-solutions/js/acid-base-solutions-config.js: 15-36
     /Users/samreid/github/balloons-and-static-electricity/js/balloons-and-static-electricity-config.js: 16-37

    - /Users/samreid/github/balloons-and-static-electricity/js/balloons-and-static-electricity/view/MinusChargeNode.js: 4-25
     /Users/samreid/github/balloons-and-static-electricity/js/balloons-and-static-electricity/view/PlusChargeNode.js: 4-25

    - /Users/samreid/github/balloons-and-static-electricity/js/balloons-and-static-electricity/accessibility/BalloonDescriber.js: 472-490
     /Users/samreid/github/balloons-and-static-electricity/js/balloons-and-static-electricity/view/SweaterNode.js: 125-143

    - /Users/samreid/github/beers-law-lab/js/beerslaw/view/ATDetectorNode.js: 14-33
     /Users/samreid/github/beers-law-lab/js/concentration/view/ConcentrationMeterNode.js: 21-40

    - /Users/samreid/github/beers-law-lab/js/beerslaw/view/ATDetectorNode.js: 135-148
     /Users/samreid/github/beers-law-lab/js/concentration/view/ConcentrationMeterNode.js: 173-186

    - /Users/samreid/github/beers-law-lab/js/beerslaw/view/ATDetectorNode.js: 267-276
     /Users/samreid/github/beers-law-lab/js/concentration/view/ConcentrationMeterNode.js: 341-350

    - /Users/samreid/github/beers-law-lab/js/beerslaw/view/SolutionComboBox.js: 4-19
     /Users/samreid/github/beers-law-lab/js/concentration/view/SoluteComboBox.js: 4-19

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/bending-light/js/bending-light-config.js: 12-45

    - /Users/samreid/github/bending-light/js/prisms/model/Polygon.js: 113-143
     /Users/samreid/github/bending-light/js/prisms/model/SemiCircle.js: 96-126

    - /Users/samreid/github/build-a-molecule/js/build-a-molecule-config.js: 10-51
     /Users/samreid/github/build-a-molecule/js/build-a-molecule-dev-config.js: 10-51

    - /Users/samreid/github/build-a-molecule/js/control/MultipleCollectionBoxNode.js: 5-24
     /Users/samreid/github/build-a-molecule/js/control/SingleCollectionBoxNode.js: 4-23

    - /Users/samreid/github/build-a-molecule/js/model/StrippedMolecule.js: 107-114
     /Users/samreid/github/build-a-molecule/js/model/StrippedMolecule.js: 143-150

    - /Users/samreid/github/build-a-molecule/js/model/MoleculeStructure.js: 381-392
     /Users/samreid/github/build-a-molecule/js/model/StrippedMolecule.js: 209-220

    - /Users/samreid/github/build-a-molecule/js/screens/CollectMultipleScreen.js: 4-26
     /Users/samreid/github/build-a-molecule/js/screens/MakeMoleculeScreen.js: 4-26

    - /Users/samreid/github/build-a-molecule/js/screens/CollectMultipleScreen.js: 38-45
     /Users/samreid/github/build-a-molecule/js/screens/MakeMoleculeScreen.js: 42-49

    - /Users/samreid/github/build-a-molecule/js/view/AtomNode.js: 55-66
     /Users/samreid/github/build-a-molecule/js/view/AtomNode.js: 94-105

    - /Users/samreid/github/build-a-molecule/js/view/AtomNode.js: 123-130
     /Users/samreid/github/build-a-molecule/js/view/AtomNode.js: 139-146

    - /Users/samreid/github/build-a-molecule/js/view/AtomNode.js: 112-121
     /Users/samreid/github/build-a-molecule/js/view/view3d/Molecule3DNode.js: 233-242

    - /Users/samreid/github/area-builder/js/area-builder-config.js: 18-37
     /Users/samreid/github/build-an-atom/js/build-an-atom-config.js: 13-32

    - /Users/samreid/github/build-an-atom/js/game/model/AtomValuePool.js: 22-89
     /Users/samreid/github/build-an-atom/js/game/model/AtomValuePool.js: 56-123

    - /Users/samreid/github/build-an-atom/js/game/model/AtomValuePool.js: 54-118
     /Users/samreid/github/build-an-atom/js/game/model/AtomValuePool.js: 150-214

    - /Users/samreid/github/build-an-atom/js/game/model/BAAGameProblem.js: 65-86
     /Users/samreid/github/build-an-atom/js/game/model/ToElementProblem.js: 56-77

    - /Users/samreid/github/build-an-atom/js/game/view/CountsToChargeProblemView.js: 5-21
     /Users/samreid/github/build-an-atom/js/game/view/CountsToMassNumberProblemView.js: 5-21

    - /Users/samreid/github/build-an-atom/js/game/view/CountsToChargeProblemView.js: 59-73
     /Users/samreid/github/build-an-atom/js/game/view/CountsToMassNumberProblemView.js: 56-70

    - /Users/samreid/github/build-an-atom/js/game/view/CountsToChargeProblemView.js: 75-91
     /Users/samreid/github/build-an-atom/js/game/view/SchematicToChargeProblemView.js: 87-103

    - /Users/samreid/github/build-an-atom/js/game/view/SchematicToChargeProblemView.js: 6-24
     /Users/samreid/github/build-an-atom/js/game/view/SchematicToMassNumberProblemView.js: 6-24

    - /Users/samreid/github/build-an-atom/js/game/view/CountsToMassNumberProblemView.js: 72-88
     /Users/samreid/github/build-an-atom/js/game/view/SchematicToMassNumberProblemView.js: 85-101

    - /Users/samreid/github/build-an-atom/js/game/view/CountsToSymbolProblemView.js: 29-42
     /Users/samreid/github/build-an-atom/js/game/view/SchematicToSymbolProblemView.js: 32-45

    - /Users/samreid/github/build-an-atom/js/game/view/CountsToSymbolProblemView.js: 58-76
     /Users/samreid/github/build-an-atom/js/game/view/SchematicToSymbolProblemView.js: 70-88

    - /Users/samreid/github/build-an-atom/js/game/view/InteractiveSymbolNode.js: 60-75
     /Users/samreid/github/build-an-atom/js/symbol/view/SymbolNode.js: 41-56

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/calculus-grapher/js/calculus-grapher-config.js: 10-43

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-42
     /Users/samreid/github/capacitor-lab-basics/js/capacitor-lab-basics-config.js: 10-41

    - /Users/samreid/github/capacitor-lab-basics/js/common/model/circuit/ParallelCircuit.js: 77-87
     /Users/samreid/github/capacitor-lab-basics/js/common/model/circuit/ParallelCircuit.js: 94-104

    - /Users/samreid/github/capacitor-lab-basics/js/common/model/wire/BatteryToSwitchWire.js: 4-21
     /Users/samreid/github/capacitor-lab-basics/js/common/model/wire/LightBulbToSwitchWire.js: 4-21

    - /Users/samreid/github/capacitor-lab-basics/js/common/view/drag/PlateAreaDragHandleNode.js: 6-23
     /Users/samreid/github/capacitor-lab-basics/js/common/view/drag/PlateSeparationDragHandleNode.js: 5-22

    - /Users/samreid/github/capacitor-lab-basics/js/common/view/drag/PlateAreaDragHandleNode.js: 75-95
     /Users/samreid/github/capacitor-lab-basics/js/common/view/drag/PlateSeparationDragHandleNode.js: 72-92

    - /Users/samreid/github/capacitor-lab-basics/js/capacitance/model/CapacitanceModel.js: 73-114
     /Users/samreid/github/capacitor-lab-basics/js/light-bulb/model/CLBLightBulbModel.js: 127-168

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/charges-and-fields/js/charges-and-fields-config.js: 9-42

    - /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricFieldCanvasNode.js: 62-77
     /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricPotentialCanvasNode.js: 49-64

    - /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricFieldCanvasNode.js: 79-89
     /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricPotentialCanvasNode.js: 64-74

    - /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricFieldCanvasNode.js: 110-124
     /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricPotentialCanvasNode.js: 102-116

    - /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricPotentialCanvasNode.js: 47-57
     /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricPotentialMobileWebGLNode.js: 42-52

    - /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ChargesAndFieldsToolboxPanel.js: 166-177
     /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricPotentialSensorNode.js: 84-95

    - /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricPotentialMobileWebGLNode.js: 39-52
     /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricPotentialWebGLNode.js: 72-85

    - /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricPotentialMobileWebGLNode.js: 133-147
     /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricPotentialWebGLNode.js: 237-251

    - /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricPotentialMobileWebGLNode.js: 163-172
     /Users/samreid/github/charges-and-fields/js/charges-and-fields/view/ElectricPotentialWebGLNode.js: 371-380

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-42
     /Users/samreid/github/circuit-construction-kit-black-box-study/js/circuit-construction-kit-black-box-study-config.js: 10-41

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/color-vision/js/color-vision-config.js: 11-44

    - /Users/samreid/github/color-vision/js/rgb/model/RGBPhotonBeam.js: 80-88
     /Users/samreid/github/color-vision/js/singlebulb/model/SingleBulbPhotonBeam.js: 154-162

    - /Users/samreid/github/beers-law-lab/js/beers-law-lab-config.js: 11-39
     /Users/samreid/github/concentration/js/concentration-config.js: 11-39

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/curve-fitting/js/curve-fitting-config.js: 10-43

    - /Users/samreid/github/curve-fitting/js/curve-fitting/view/BarometerR2Node.js: 11-20
     /Users/samreid/github/curve-fitting/js/curve-fitting/view/BarometerX2Node.js: 15-24

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/energy-forms-and-changes/js/energy-forms-and-changes-config.js: 11-44

    - /Users/samreid/github/energy-forms-and-changes/js/energy-systems/model/BeakerHeater.js: 38-47
     /Users/samreid/github/energy-forms-and-changes/js/energy-systems/model/LightBulb.js: 36-45

    - /Users/samreid/github/energy-forms-and-changes/js/energy-systems/model/BeakerHeater.js: 335-355
     /Users/samreid/github/energy-forms-and-changes/js/energy-systems/model/LightBulb.js: 332-352

    - /Users/samreid/github/energy-forms-and-changes/js/energy-systems/model/SunEnergySource.js: 14-25
     /Users/samreid/github/energy-forms-and-changes/js/energy-systems/model/TeaPot.js: 14-25

    - /Users/samreid/github/energy-forms-and-changes/js/energy-systems/view/FluorescentBulbNode.js: 29-47
     /Users/samreid/github/energy-forms-and-changes/js/energy-systems/view/IncandescentBulbNode.js: 28-46

    - /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/DebugTracks.js: 37-45
     /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/DebugTracks.js: 51-59

    - /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/DebugTracks.js: 68-73
     /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/DebugTracks.js: 82-87

    - /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/DebugTracks.js: 110-115
     /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/DebugTracks.js: 124-129

    - /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/DebugTracks.js: 138-143
     /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/DebugTracks.js: 153-158

    - /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/DebugTracks.js: 136-145
     /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/DebugTracks.js: 165-174

    - /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/DebugTracks.js: 189-201
     /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/DebugTracks.js: 202-214

    - /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/Track.js: 653-664
     /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/Track.js: 676-687

    - /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/view/FrictionControl.js: 4-21
     /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/view/MassControlPanel.js: 4-21

    - /Users/samreid/github/estimation/js/common/view/CubeBackView.js: 29-45
     /Users/samreid/github/estimation/js/common/view/CubeView.js: 37-53

    - /Users/samreid/github/estimation/js/explore/model/CubeExplorationMode.js: 89-114
     /Users/samreid/github/estimation/js/explore/model/CylinderExplorationMode.js: 78-103

    - /Users/samreid/github/estimation/js/explore/model/CubeExplorationMode.js: 93-110
     /Users/samreid/github/estimation/js/explore/model/LineExplorationMode.js: 58-75

    - /Users/samreid/github/estimation/js/explore/model/CubeExplorationMode.js: 87-114
     /Users/samreid/github/estimation/js/explore/model/RectangleExplorationMode.js: 79-106

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/example-sim/js/example-sim-config.js: 11-44

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 23-42
     /Users/samreid/github/expression-exchange/js/expression-exchange-config.js: 23-42

    - /Users/samreid/github/area-builder/js/game/view/StartGameLevelNode.js: 14-27
     /Users/samreid/github/expression-exchange/js/game/view/StartGameLevelNode.js: 14-27

    - /Users/samreid/github/area-builder/js/game/view/StartGameLevelNode.js: 26-57
     /Users/samreid/github/expression-exchange/js/game/view/StartGameLevelNode.js: 26-57

    - /Users/samreid/github/area-builder/js/game/view/StartGameLevelNode.js: 58-129
     /Users/samreid/github/expression-exchange/js/game/view/StartGameLevelNode.js: 59-130

    - /Users/samreid/github/expression-exchange/js/explore/view/EEExploreIconNode.js: 4-20
     /Users/samreid/github/expression-exchange/js/variables/view/EEVariablesIconNode.js: 4-20

    - /Users/samreid/github/capacitor-lab-basics/js/common/view/BulbNode.js: 127-184
     /Users/samreid/github/faradays-law/js/faradays-law/view/BulbNode.js: 40-97

    - /Users/samreid/github/faradays-law/js/faradays-law/view/CoilsWiresNode.js: 43-52
     /Users/samreid/github/faradays-law/js/faradays-law/view/CoilsWiresNode.js: 58-67

    - /Users/samreid/github/fluid-pressure-and-flow/js/flow/model/Pipe.js: 307-321
     /Users/samreid/github/fluid-pressure-and-flow/js/flow/model/Pipe.js: 329-343

    - /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/model/SplineEvaluation.js: 20-63
     /Users/samreid/github/fluid-pressure-and-flow/js/flow/model/SplineEvaluation.js: 16-59

    - /Users/samreid/github/fluid-pressure-and-flow/js/flow/view/ParticleCanvasNode.js: 45-53
     /Users/samreid/github/fluid-pressure-and-flow/js/flow/view/ParticleCanvasNode.js: 56-64

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/fluid-pressure-and-flow/js/fluid-pressure-and-flow-config.js: 10-43

    - /Users/samreid/github/fluid-pressure-and-flow/js/under-pressure/view/ChamberPoolBack.js: 72-84
     /Users/samreid/github/fluid-pressure-and-flow/js/under-pressure/view/ChamberPoolWaterNode.js: 25-37

    - /Users/samreid/github/fluid-pressure-and-flow/js/under-pressure/view/ChamberPoolBack.js: 118-130
     /Users/samreid/github/fluid-pressure-and-flow/js/under-pressure/view/ChamberPoolWaterNode.js: 50-62

    - /Users/samreid/github/fluid-pressure-and-flow/js/under-pressure/view/ChamberPoolBack.js: 6-19
     /Users/samreid/github/fluid-pressure-and-flow/js/under-pressure/view/SquarePoolBack.js: 6-19

    - /Users/samreid/github/fluid-pressure-and-flow/js/under-pressure/view/SquarePoolBack.js: 4-26
     /Users/samreid/github/fluid-pressure-and-flow/js/under-pressure/view/TrapezoidPoolBack.js: 5-27

    - /Users/samreid/github/fluid-pressure-and-flow/js/watertower/view/HoseNode.js: 156-164
     /Users/samreid/github/fluid-pressure-and-flow/js/watertower/view/HoseNode.js: 192-200

    - /Users/samreid/github/fluid-pressure-and-flow/js/watertower/view/HoseNode.js: 181-189
     /Users/samreid/github/fluid-pressure-and-flow/js/watertower/view/HoseNode.js: 217-225

    - /Users/samreid/github/fluid-pressure-and-flow/js/flow/view/FlowToolsControlPanel.js: 4-23
     /Users/samreid/github/fluid-pressure-and-flow/js/watertower/view/ToolsControlPanel.js: 4-23

    - /Users/samreid/github/fluid-pressure-and-flow/js/watertower/view/WaterTowerNode.js: 72-77
     /Users/samreid/github/fluid-pressure-and-flow/js/watertower/view/WaterTowerNode.js: 173-178

    - /Users/samreid/github/forces-and-motion-basics/js/motion/view/MotionControlPanel.js: 217-224
     /Users/samreid/github/forces-and-motion-basics/js/motion/view/MotionControlPanel.js: 254-261

    - /Users/samreid/github/forces-and-motion-basics/js/netforce/model/NetForceModel.js: 441-451
     /Users/samreid/github/forces-and-motion-basics/js/netforce/model/NetForceModel.js: 470-480

    - /Users/samreid/github/forces-and-motion-basics/js/motion/view/ItemToolboxNode.js: 51-70
     /Users/samreid/github/forces-and-motion-basics/js/netforce/view/PullerToolboxNode.js: 60-79

    - /Users/samreid/github/forces-and-motion-basics/js/motion/view/ItemToolboxNode.js: 88-128
     /Users/samreid/github/forces-and-motion-basics/js/netforce/view/PullerToolboxNode.js: 100-140

    - /Users/samreid/github/forces-and-motion-basics/js/motion/view/ItemToolboxNode.js: 130-152
     /Users/samreid/github/forces-and-motion-basics/js/netforce/view/PullerToolboxNode.js: 139-161

    - /Users/samreid/github/estimation/js/estimation-config.js: 11-38
     /Users/samreid/github/fraction-comparison/js/fraction-comparison-config.js: 10-37

    - /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/view/EnergySkateParkBasicsScreenView.js: 290-303
     /Users/samreid/github/fraction-comparison/js/intro/view/CompareSeparateButton.js: 25-38

    - /Users/samreid/github/fraction-comparison/js/intro/view/HorizontalBarContainerNode.js: 134-140
     /Users/samreid/github/fraction-comparison/js/intro/view/HorizontalBarContainerNode.js: 145-151

    - /Users/samreid/github/expression-exchange/js/expression-exchange-config.js: 16-42
     /Users/samreid/github/fraction-matcher/js/fraction-matcher-config.js: 20-46

    - /Users/samreid/github/fraction-matcher/js/model/Constants.js: 116-133
     /Users/samreid/github/fraction-matcher/js/model/Constants.js: 139-156

    - /Users/samreid/github/fraction-matcher/js/shapes/Pattern.js: 41-55
     /Users/samreid/github/fraction-matcher/js/shapes/Pattern.js: 59-73

    - /Users/samreid/github/fraction-matcher/js/shapes/Pattern.js: 87-100
     /Users/samreid/github/fraction-matcher/js/shapes/Pattern.js: 126-139

    - /Users/samreid/github/fraction-matcher/js/shapes/Pattern.js: 124-132
     /Users/samreid/github/fraction-matcher/js/shapes/Pattern.js: 164-172

    - /Users/samreid/github/fraction-matcher/js/shapes/Pattern.js: 169-181
     /Users/samreid/github/fraction-matcher/js/shapes/Pattern.js: 328-340

    - /Users/samreid/github/fraction-matcher/js/shapes/Pattern.js: 41-55
     /Users/samreid/github/fraction-matcher/js/shapes/Pattern.js: 473-487

    - /Users/samreid/github/fraction-matcher/js/shapes/Pattern.js: 41-55
     /Users/samreid/github/fraction-matcher/js/shapes/Pattern.js: 538-552

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 23-44
     /Users/samreid/github/friction/js/friction-config.js: 16-37

    - /Users/samreid/github/function-builder/js/common/view/equations/HelpfulEquationNode.js: 136-154
     /Users/samreid/github/function-builder/js/common/view/equations/HelpfulEquationNode.js: 162-180

    - /Users/samreid/github/area-builder/js/area-builder-config.js: 10-39
     /Users/samreid/github/function-builder/js/function-builder-config.js: 11-40

    - /Users/samreid/github/function-builder/js/equations/model/EquationsScene.js: 16-30
     /Users/samreid/github/function-builder/js/mystery/model/MysteryScene.js: 20-34

    - /Users/samreid/github/function-builder/js/equations/model/EquationsScene.js: 12-30
     /Users/samreid/github/function-builder/js/numbers/model/NumbersScene.js: 11-29

    - /Users/samreid/github/function-builder/js/equations/model/EquationsScene.js: 57-71
     /Users/samreid/github/function-builder/js/numbers/model/NumbersScene.js: 63-77

    - /Users/samreid/github/function-builder/js/patterns/model/PatternsScene.js: 21-33
     /Users/samreid/github/function-builder/js/test/view/testImageFunctions.js: 12-24

    - /Users/samreid/github/function-builder/js/patterns/model/PatternsScene.js: 35-49
     /Users/samreid/github/function-builder/js/test/view/testImageFunctions.js: 42-56

    - /Users/samreid/github/gene-expression-essentials/js/common/model/MobileBiomolecule.js: 278-293
     /Users/samreid/github/gene-expression-essentials/js/common/model/Ribosome.js: 195-210

    - /Users/samreid/github/gene-expression-essentials/js/common/model/BioShapeUtils.js: 94-101
     /Users/samreid/github/gene-expression-essentials/js/common/model/ShapeUtils.js: 32-39

    - /Users/samreid/github/gene-expression-essentials/js/common/model/WindingBiomolecule.js: 71-87
     /Users/samreid/github/gene-expression-essentials/js/common/model/WindingBiomolecule.js: 384-400

    - /Users/samreid/github/gene-expression-essentials/js/manualgeneexpression/model/ProteinB.js: 44-82
     /Users/samreid/github/gene-expression-essentials/js/manualgeneexpression/model/ProteinC.js: 45-83

    - /Users/samreid/github/gene-expression-essentials/js/manualgeneexpression/view/ManualGeneExpressionScreenView.js: 69-84
     /Users/samreid/github/gene-expression-essentials/js/mrnaproduction/view/MessengerRnaProductionScreenView.js: 74-89

    - /Users/samreid/github/graphing-lines/js/common/view/manipulator/SlopeManipulator.js: 4-23
     /Users/samreid/github/graphing-lines/js/common/view/manipulator/X1Y1Manipulator.js: 4-23

    - /Users/samreid/github/graphing-lines/js/common/view/manipulator/SlopeManipulator.js: 4-23
     /Users/samreid/github/graphing-lines/js/common/view/manipulator/X2Y2Manipulator.js: 4-23

    - /Users/samreid/github/graphing-lines/js/common/view/manipulator/SlopeManipulator.js: 55-69
     /Users/samreid/github/graphing-lines/js/common/view/manipulator/X2Y2Manipulator.js: 55-69

    - /Users/samreid/github/graphing-lines/js/common/view/manipulator/SlopeManipulator.js: 4-23
     /Users/samreid/github/graphing-lines/js/common/view/manipulator/YInterceptManipulator.js: 5-24

    - /Users/samreid/github/graphing-lines/js/common/view/manipulator/X1Y1Manipulator.js: 57-75
     /Users/samreid/github/graphing-lines/js/common/view/manipulator/YInterceptManipulator.js: 54-72

    - /Users/samreid/github/area-builder/js/area-builder-config.js: 10-39
     /Users/samreid/github/graphing-lines/js/graphing-lines-config.js: 11-40

    - /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory1.js: 5-22
     /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory2.js: 6-23

    - /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory1.js: 114-130
     /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory2.js: 132-148

    - /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory1.js: 129-146
     /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory2.js: 147-164

    - /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory2.js: 118-126
     /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory3.js: 55-63

    - /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory2.js: 13-26
     /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory4.js: 12-25

    - /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory2.js: 114-129
     /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory4.js: 53-68

    - /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory5.js: 140-152
     /Users/samreid/github/graphing-lines/js/linegame/model/ChallengeFactory6.js: 86-98

    - /Users/samreid/github/graphing-lines/js/pointslope/view/PointSlopeEquationNode.js: 10-33
     /Users/samreid/github/graphing-lines/js/slopeintercept/view/SlopeInterceptEquationNode.js: 13-36

    - /Users/samreid/github/graphing-lines/js/pointslope/view/PointSlopeEquationNode.js: 68-76
     /Users/samreid/github/graphing-lines/js/slopeintercept/view/SlopeInterceptEquationNode.js: 70-78

    - /Users/samreid/github/graphing-lines/js/pointslope/view/PointSlopeEquationNode.js: 106-119
     /Users/samreid/github/graphing-lines/js/slopeintercept/view/SlopeInterceptEquationNode.js: 97-110

    - /Users/samreid/github/graphing-lines/js/pointslope/view/PointSlopeEquationNode.js: 148-164
     /Users/samreid/github/graphing-lines/js/slopeintercept/view/SlopeInterceptEquationNode.js: 137-153

    - /Users/samreid/github/graphing-lines/js/pointslope/view/PointSlopeEquationNode.js: 262-278
     /Users/samreid/github/graphing-lines/js/slopeintercept/view/SlopeInterceptEquationNode.js: 193-209

    - /Users/samreid/github/graphing-lines/js/pointslope/view/PointSlopeEquationNode.js: 361-379
     /Users/samreid/github/graphing-lines/js/slopeintercept/view/SlopeInterceptEquationNode.js: 378-396

    - /Users/samreid/github/area-builder/js/area-builder-config.js: 10-43
     /Users/samreid/github/graphing-quadratics/js/graphing-quadratics-config.js: 11-44

    - /Users/samreid/github/graphing-quadratics/js/decimals/view/DecimalsView.js: 4-21
     /Users/samreid/github/graphing-quadratics/js/integers/view/IntegersView.js: 4-21

    - /Users/samreid/github/graphing-quadratics/js/decimals/view/DecimalsView.js: 4-21
     /Users/samreid/github/graphing-quadratics/js/vertexform/view/VertexFormView.js: 4-21

    - /Users/samreid/github/gravity-and-orbits/js/common/module/ModeList.js: 400-412
     /Users/samreid/github/gravity-and-orbits/js/common/module/ModeList.js: 427-439

    - /Users/samreid/github/build-an-atom/js/build-an-atom-config.js: 5-33
     /Users/samreid/github/gravity-force-lab/js/gravity-force-lab-config.js: 10-38

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/gravity-force-lab-basics/js/gravity-force-lab-basics-config.js: 11-44

    - /Users/samreid/github/balancing-act/js/balancing-act-config.js: 25-45
     /Users/samreid/github/griddle/js/griddle-config.js: 12-32

    - /Users/samreid/github/hookes-law/js/common/view/AppliedForceControl.js: 4-22
     /Users/samreid/github/hookes-law/js/common/view/DisplacementControl.js: 4-22

    - /Users/samreid/github/hookes-law/js/common/view/DisplacementVectorNode.js: 77-90
     /Users/samreid/github/hookes-law/js/common/view/ForceVectorNode.js: 93-106

    - /Users/samreid/github/hookes-law/js/common/view/AppliedForceControl.js: 4-22
     /Users/samreid/github/hookes-law/js/common/view/SpringConstantControl.js: 4-22

    - /Users/samreid/github/hookes-law/js/common/view/DisplacementControl.js: 50-59
     /Users/samreid/github/hookes-law/js/common/view/SpringConstantControl.js: 48-57

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/hookes-law/js/hookes-law-config.js: 11-44

    - /Users/samreid/github/hookes-law/js/energy/view/EnergySystemNode.js: 15-27
     /Users/samreid/github/hookes-law/js/intro/view/IntroSystemNode.js: 14-26

    - /Users/samreid/github/hookes-law/js/energy/view/EnergySystemNode.js: 75-90
     /Users/samreid/github/hookes-law/js/intro/view/IntroSystemNode.js: 78-93

    - /Users/samreid/github/hookes-law/js/energy/view/EnergySystemNode.js: 125-140
     /Users/samreid/github/hookes-law/js/intro/view/IntroSystemNode.js: 135-150

    - /Users/samreid/github/hookes-law/js/systems/view/ParallelSpringControls.js: 73-88
     /Users/samreid/github/hookes-law/js/systems/view/SeriesSpringControls.js: 70-85

    - /Users/samreid/github/hookes-law/js/intro/view/IntroSystemNode.js: 5-26
     /Users/samreid/github/hookes-law/js/systems/view/SeriesSystemNode.js: 4-25

    - /Users/samreid/github/hookes-law/js/systems/view/ParallelSystemNode.js: 188-203
     /Users/samreid/github/hookes-law/js/systems/view/SeriesSystemNode.js: 189-204

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-41
     /Users/samreid/github/isotopes-and-atomic-mass/js/isotopes-and-atomic-mass-config.js: 10-40

    - /Users/samreid/github/build-an-atom/js/common/model/BuildAnAtomModel.js: 36-41
     /Users/samreid/github/isotopes-and-atomic-mass/js/make-isotopes/model/MakeIsotopesModel.js: 34-39

    - /Users/samreid/github/build-an-atom/js/common/view/AtomView.js: 123-133
     /Users/samreid/github/isotopes-and-atomic-mass/js/make-isotopes/view/InteractiveIsotopeNode.js: 89-99

    - /Users/samreid/github/isotopes-and-atomic-mass/js/mix-isotopes/model/MixIsotopesModel.js: 397-410
     /Users/samreid/github/isotopes-and-atomic-mass/js/mix-isotopes/model/MixIsotopesModel.js: 409-422

    - /Users/samreid/github/balloons-and-static-electricity/js/balloons-and-static-electricity/view/MinusChargeNode.js: 29-42
     /Users/samreid/github/john-travoltage/js/john-travoltage/view/ElectronNode.js: 31-44

    - /Users/samreid/github/john-travoltage/js/john-travoltage/view/ElectronNode.js: 146-156
     /Users/samreid/github/john-travoltage/js/john-travoltage/view/ElectronNode.js: 169-179

    - /Users/samreid/github/griddle/js/griddle-config.js: 4-32
     /Users/samreid/github/joist/js/joist-config.js: 4-32

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/least-squares-regression/js/least-squares-regression-config.js: 10-43

    - /Users/samreid/github/graphing-lines/js/common/view/GraphNode.js: 97-134
     /Users/samreid/github/least-squares-regression/js/least-squares-regression/view/GraphAxesNode.js: 96-133

    - /Users/samreid/github/graphing-lines/js/common/view/GraphNode.js: 135-151
     /Users/samreid/github/least-squares-regression/js/least-squares-regression/view/GraphAxesNode.js: 133-149

    - /Users/samreid/github/expression-exchange/js/expression-exchange-config.js: 10-44
     /Users/samreid/github/make-a-ten/js/make-a-ten-config.js: 10-44

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/models-of-the-hydrogen-atom/js/models-of-the-hydrogen-atom-config.js: 11-44

    - /Users/samreid/github/beers-law-lab/js/beers-law-lab-config.js: 11-39
     /Users/samreid/github/molarity/js/molarity-config.js: 10-38

    - /Users/samreid/github/beers-law-lab/js/concentration/view/SaturatedIndicator.js: 32-47
     /Users/samreid/github/molarity/js/molarity/view/SaturatedIndicator.js: 30-45

    - /Users/samreid/github/beers-law-lab/js/concentration/view/SoluteComboBox.js: 64-76
     /Users/samreid/github/molarity/js/molarity/view/SoluteComboBox.js: 60-72

    - /Users/samreid/github/beers-law-lab/js/beers-law-lab-config.js: 11-41
     /Users/samreid/github/molecule-polarity/js/molecule-polarity-config.js: 10-40

    - /Users/samreid/github/molecule-polarity/js/threeatoms/model/TriatomicMolecule.js: 4-20
     /Users/samreid/github/molecule-polarity/js/twoatoms/model/DiatomicMolecule.js: 4-20

    - /Users/samreid/github/molecule-polarity/js/twoatoms/view/ElectronDensityNode.js: 5-35
     /Users/samreid/github/molecule-polarity/js/twoatoms/view/ElectrostaticPotentialNode.js: 5-35

    - /Users/samreid/github/molecule-polarity/js/twoatoms/view/ElectronDensityNode.js: 47-76
     /Users/samreid/github/molecule-polarity/js/twoatoms/view/ElectrostaticPotentialNode.js: 47-76

    - /Users/samreid/github/molecule-polarity/js/twoatoms/view/ElectronDensityNode.js: 77-96
     /Users/samreid/github/molecule-polarity/js/twoatoms/view/ElectrostaticPotentialNode.js: 77-96

    - /Users/samreid/github/molecule-polarity/js/twoatoms/view/ElectronDensityNode.js: 97-107
     /Users/samreid/github/molecule-polarity/js/twoatoms/view/ElectrostaticPotentialNode.js: 96-106

    - /Users/samreid/github/molecule-polarity/js/threeatoms/view/ThreeAtomsControlPanel.js: 79-96
     /Users/samreid/github/molecule-polarity/js/twoatoms/view/TwoAtomsControlPanel.js: 94-111

    - /Users/samreid/github/molecule-shapes/js/common/view/3d/BondAngleFallbackView.js: 87-101
     /Users/samreid/github/molecule-shapes/js/common/view/3d/BondAngleWebGLView.js: 180-194

    - /Users/samreid/github/molecule-shapes/js/common/model/VSEPRConfiguration.js: 22-35
     /Users/samreid/github/molecule-shapes/js/common/view/GeometryNamePanel.js: 34-47

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-42
     /Users/samreid/github/molecule-shapes/js/molecule-shapes-config.js: 11-42

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-53
     /Users/samreid/github/molecule-shapes/js/molecule-shapes-dev-config.js: 11-53

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-45
     /Users/samreid/github/molecule-shapes-basics/js/molecule-shapes-basics-config.js: 11-45

    - /Users/samreid/github/molecule-shapes/js/molecule-shapes-main.js: 2-23
     /Users/samreid/github/molecule-shapes-basics/js/molecule-shapes-basics-main.js: 2-23

    - /Users/samreid/github/molecules-and-light/js/photon-absorption/model/molecules/CO.js: 4-22
     /Users/samreid/github/molecules-and-light/js/photon-absorption/model/molecules/H2O.js: 4-22

    - /Users/samreid/github/molecules-and-light/js/photon-absorption/model/molecules/NO2.js: 166-178
     /Users/samreid/github/molecules-and-light/js/photon-absorption/model/molecules/O3.js: 160-172

    - /Users/samreid/github/molecules-and-light/js/photon-absorption/view/AtomicBondNode.js: 144-152
     /Users/samreid/github/molecules-and-light/js/photon-absorption/view/AtomicBondNode.js: 162-170

    - /Users/samreid/github/gene-expression-essentials/js/gene-expression-essentials-config.js: 20-45
     /Users/samreid/github/neuron/js/neuron-config.js: 17-42

    - /Users/samreid/github/neuron/js/neuron/model/ModifiedHodgkinHuxleyModel.js: 92-97
     /Users/samreid/github/neuron/js/neuron/model/ModifiedHodgkinHuxleyModel.js: 126-131

    - /Users/samreid/github/neuron/js/neuron/model/DualGateChannelTraversalMotionStrategy.js: 247-254
     /Users/samreid/github/neuron/js/neuron/model/TraverseChannelAndFadeMotionStrategy.js: 189-196

    - /Users/samreid/github/neuron/js/neuron/view/ParticlesCanvasNode.js: 68-77
     /Users/samreid/github/neuron/js/neuron/view/ParticlesCanvasNode.js: 86-95

    - /Users/samreid/github/build-an-atom/js/build-an-atom-config.js: 5-35
     /Users/samreid/github/ohms-law/js/ohms-law-config.js: 8-38

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/pendulum-lab/js/pendulum-lab-config.js: 10-43

    - /Users/samreid/github/beers-law-lab/js/concentration/view/StockSolutionNode.js: 34-45
     /Users/samreid/github/ph-scale/js/common/view/DropperFluidNode.js: 30-41

    - /Users/samreid/github/beers-law-lab/js/concentration/view/FaucetFluidNode.js: 33-47
     /Users/samreid/github/ph-scale/js/common/view/FaucetFluidNode.js: 29-43

    - /Users/samreid/github/ph-scale/js/common/view/graph/LinearGraph.js: 166-189
     /Users/samreid/github/ph-scale/js/common/view/graph/LogarithmicGraph.js: 166-189

    - /Users/samreid/github/beers-law-lab/js/concentration/view/SolutionNode.js: 36-52
     /Users/samreid/github/ph-scale/js/common/view/SolutionNode.js: 30-46

    - /Users/samreid/github/beers-law-lab/js/concentration/view/ConcentrationMeterNode.js: 337-345
     /Users/samreid/github/ph-scale/js/macro/view/MacroPHMeterNode.js: 395-403

    - /Users/samreid/github/ph-scale/js/macro/view/MacroView.js: 47-59
     /Users/samreid/github/ph-scale/js/micro/view/MicroView.js: 57-69

    - /Users/samreid/github/ph-scale/js/micro/view/MicroView.js: 47-58
     /Users/samreid/github/ph-scale/js/mysolution/view/MySolutionView.js: 38-49

    - /Users/samreid/github/ph-scale/js/micro/view/MicroView.js: 71-87
     /Users/samreid/github/ph-scale/js/mysolution/view/MySolutionView.js: 48-64

    - /Users/samreid/github/estimation/js/estimation-config.js: 11-40
     /Users/samreid/github/ph-scale/js/ph-scale-config.js: 10-39

    - /Users/samreid/github/estimation/js/common/view/CylinderView.js: 49-55
     /Users/samreid/github/plinko-probability/js/intro/view/CylindersFrontNode.js: 42-48

    - /Users/samreid/github/area-builder/js/area-builder-config.js: 10-39
     /Users/samreid/github/plinko-probability/js/plinko-probability-config.js: 10-39

    - /Users/samreid/github/energy-skate-park-basics/js/energy-skate-park-basics/view/EnergySkateParkBasicsScreenView.js: 440-463
     /Users/samreid/github/projectile-motion/js/common/view/ProjectileMotionScreenView.js: 267-290

    - /Users/samreid/github/projectile-motion/js/common/view/ToolboxPanel.js: 87-105
     /Users/samreid/github/projectile-motion/js/common/view/ToolboxPanel.js: 149-167

    - /Users/samreid/github/projectile-motion/js/common/view/TracerNode.js: 87-102
     /Users/samreid/github/projectile-motion/js/common/view/TracerNode.js: 226-241

    - /Users/samreid/github/neuron/js/neuron/view/ZoomControl.js: 35-47
     /Users/samreid/github/projectile-motion/js/common/view/ZoomControl.js: 34-46

    - /Users/samreid/github/neuron/js/neuron/view/ZoomControl.js: 49-97
     /Users/samreid/github/projectile-motion/js/common/view/ZoomControl.js: 46-94

    - /Users/samreid/github/projectile-motion/js/drag/view/ProjectilePanel.js: 22-46
     /Users/samreid/github/projectile-motion/js/intro/view/IntroSecondPanel.js: 21-45

    - /Users/samreid/github/projectile-motion/js/intro/view/IntroSecondPanel.js: 125-143
     /Users/samreid/github/projectile-motion/js/lab/view/LabSecondPanel.js: 90-108

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/projectile-motion/js/projectile-motion-config.js: 10-43

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/proportion-playground/js/proportion-playground-config.js: 11-44

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/protein-synthesis/js/protein-synthesis-config.js: 10-43

    - /Users/samreid/github/protein-synthesis/js/protein-synthesis/view/TRNANode.js: 117-129
     /Users/samreid/github/protein-synthesis/js/protein-synthesis/view/TRNANode.js: 145-157

    - /Users/samreid/github/balancing-chemical-equations/js/common/model/MoleculeFactory.js: 28-50
     /Users/samreid/github/reactants-products-and-leftovers/js/common/model/ReactionFactory.js: 35-57

    - /Users/samreid/github/reactants-products-and-leftovers/js/common/model/ReactionFactory.js: 23-60
     /Users/samreid/github/reactants-products-and-leftovers/js/common/view/MoleculeNodes.js: 15-52

    - /Users/samreid/github/reactants-products-and-leftovers/js/common/model/Reaction.js: 54-62
     /Users/samreid/github/reactants-products-and-leftovers/js/game/model/GameGuess.js: 71-79

    - /Users/samreid/github/graphing-lines/js/linegame/model/LineGameModel.js: 226-250
     /Users/samreid/github/reactants-products-and-leftovers/js/game/model/GameModel.js: 202-226

    - /Users/samreid/github/beers-law-lab/js/beers-law-lab-config.js: 19-37
     /Users/samreid/github/reactants-products-and-leftovers/js/reactants-products-and-leftovers-config.js: 20-38

    - /Users/samreid/github/build-a-molecule/js/build-a-molecule-config.js: 22-42
     /Users/samreid/github/reactants-products-and-leftovers/js/reactants-products-and-leftovers-config.js: 23-43

    - /Users/samreid/github/build-an-atom/js/build-an-atom-config.js: 10-35
     /Users/samreid/github/resistance-in-a-wire/js/resistance-in-a-wire-config.js: 13-38

    - /Users/samreid/github/ohms-law/js/ohms-law/view/Slider.js: 17-39
     /Users/samreid/github/resistance-in-a-wire/js/resistance-in-a-wire/view/Slider.js: 17-39

    - /Users/samreid/github/ohms-law/js/ohms-law/view/Slider.js: 52-63
     /Users/samreid/github/resistance-in-a-wire/js/resistance-in-a-wire/view/Slider.js: 52-63

    - /Users/samreid/github/rutherford-scattering/js/common/model/Atom.js: 99-116
     /Users/samreid/github/rutherford-scattering/js/common/model/RSBaseModel.js: 166-183

    - /Users/samreid/github/rutherford-scattering/js/common/model/Atom.js: 129-150
     /Users/samreid/github/rutherford-scattering/js/common/model/RSBaseModel.js: 200-221

    - /Users/samreid/github/charges-and-fields/js/charges-and-fields/ChargesAndFieldsColors.js: 170-180
     /Users/samreid/github/rutherford-scattering/js/common/RSColors.js: 132-142

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-40
     /Users/samreid/github/rutherford-scattering/js/rutherford-scattering-config.js: 11-40

    - /Users/samreid/github/scenery-phet/js/buttons/RewindButton.js: 6-24
     /Users/samreid/github/scenery-phet/js/buttons/StepButton.js: 8-26

    - /Users/samreid/github/scenery-phet/js/CanvasWarningNode.js: 4-22
     /Users/samreid/github/scenery-phet/js/IE11StencilWarningNode.js: 6-24

    - /Users/samreid/github/scenery-phet/js/CanvasWarningNode.js: 39-58
     /Users/samreid/github/scenery-phet/js/IE11StencilWarningNode.js: 41-60

    - /Users/samreid/github/balloons-and-static-electricity/js/balloons-and-static-electricity/accessibility/AccessibleDragNode.js: 213-234
     /Users/samreid/github/scenery-phet/js/input/MovableDragHandler.js: 93-114

    - /Users/samreid/github/griddle/js/griddle-config.js: 4-28
     /Users/samreid/github/scenery-phet/js/scenery-phet-config.js: 4-28

    - /Users/samreid/github/fraction-comparison/js/intro/view/NodeDragHandler.js: 16-55
     /Users/samreid/github/seasons/js/intensity/view/NodeDragHandler.js: 12-51

    - /Users/samreid/github/fraction-comparison/js/intro/view/NodeDragHandler.js: 56-78
     /Users/samreid/github/seasons/js/intensity/view/NodeDragHandler.js: 52-74

    - /Users/samreid/github/seasons/js/intensity/view/NodeDragHandler.js: 2-34
     /Users/samreid/github/seasons/js/intensity/view/NodeDragHandler3D.js: 2-34

    - /Users/samreid/github/seasons/js/intensity/view/NodeDragHandler.js: 42-74
     /Users/samreid/github/seasons/js/intensity/view/NodeDragHandler3D.js: 44-76

    - /Users/samreid/github/seasons/js/intensity/view/PanelNode.js: 276-285
     /Users/samreid/github/seasons/js/intensity/view/TargetOutlineNode.js: 31-40

    - /Users/samreid/github/estimation/js/estimation-config.js: 11-40
     /Users/samreid/github/seasons/js/seasons-config.js: 10-39

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/simula-rasa/js/simula-rasa-config.js: 11-44

    - /Users/samreid/github/fluid-pressure-and-flow/js/flow/view/FlowView.js: 229-244
     /Users/samreid/github/states-of-matter/js/atomic-interactions/view/AtomicInteractionsScreenView.js: 193-208

    - /Users/samreid/github/states-of-matter/js/atomic-interactions/view/InteractiveInteractionPotentialDiagram.js: 74-85
     /Users/samreid/github/states-of-matter/js/atomic-interactions/view/InteractiveInteractionPotentialDiagram.js: 115-126

    - /Users/samreid/github/states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 149-162
     /Users/samreid/github/states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 226-239

    - /Users/samreid/github/states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 263-273
     /Users/samreid/github/states-of-matter/js/common/model/engine/MonatomicPhaseStateChanger.js: 223-233

    - /Users/samreid/github/states-of-matter/js/common/model/engine/MonatomicPhaseStateChanger.js: 36-58
     /Users/samreid/github/states-of-matter/js/common/model/engine/WaterPhaseStateChanger.js: 47-69

    - /Users/samreid/github/states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 161-171
     /Users/samreid/github/states-of-matter/js/common/model/engine/WaterPhaseStateChanger.js: 100-110

    - /Users/samreid/github/states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 160-171
     /Users/samreid/github/states-of-matter/js/common/model/engine/WaterPhaseStateChanger.js: 160-171

    - /Users/samreid/github/states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 178-202
     /Users/samreid/github/states-of-matter/js/common/model/engine/WaterPhaseStateChanger.js: 179-203

    - /Users/samreid/github/states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 237-252
     /Users/samreid/github/states-of-matter/js/common/model/engine/WaterPhaseStateChanger.js: 239-254

    - /Users/samreid/github/states-of-matter/js/common/model/engine/DiatomicPhaseStateChanger.js: 262-286
     /Users/samreid/github/states-of-matter/js/common/model/engine/WaterPhaseStateChanger.js: 264-288

    - /Users/samreid/github/states-of-matter/js/common/view/ParticleImageCanvasNode.js: 143-158
     /Users/samreid/github/states-of-matter/js/common/view/ParticleImageCanvasNode.js: 162-177

    - /Users/samreid/github/states-of-matter/js/atomic-interactions/view/InteractiveInteractionPotentialDiagram.js: 111-119
     /Users/samreid/github/states-of-matter/js/phase-changes/view/EpsilonControlInteractionPotentialDiagram.js: 94-102

    - /Users/samreid/github/states-of-matter/js/atomic-interactions/view/InteractiveInteractionPotentialDiagram.js: 70-78
     /Users/samreid/github/states-of-matter/js/phase-changes/view/EpsilonControlInteractionPotentialDiagram.js: 116-124

    - /Users/samreid/github/atomic-interactions/js/atomic-interactions-config.js: 11-44
     /Users/samreid/github/states-of-matter/js/states-of-matter-config.js: 10-43

    - /Users/samreid/github/states-of-matter/js/phase-changes/view/PhaseChangesMoleculesControlPanel.js: 48-74
     /Users/samreid/github/states-of-matter/js/states/view/StatesMoleculesControlPanel.js: 34-60

    - /Users/samreid/github/states-of-matter/js/phase-changes/view/PhaseChangesScreenView.js: 140-159
     /Users/samreid/github/states-of-matter/js/states/view/StatesScreenView.js: 119-138

    - /Users/samreid/github/states-of-matter/js/phase-changes/view/PhaseChangesScreenView.js: 293-333
     /Users/samreid/github/states-of-matter/js/states/view/StatesScreenView.js: 154-194

    - /Users/samreid/github/molecules-and-light/js/molecules-and-light-config.js: 12-44
     /Users/samreid/github/states-of-matter-basics/js/states-of-matter-basics-config.js: 11-43

    - /Users/samreid/github/sun/js/buttons/RadioButtonGroup.js: 77-99
     /Users/samreid/github/sun/js/buttons/RadioButtonGroupMember.js: 38-60

    - /Users/samreid/github/sun/js/buttons/RectangularButtonView.js: 231-249
     /Users/samreid/github/sun/js/buttons/RectangularButtonView.js: 346-364

    - /Users/samreid/github/sun/js/buttons/RectangularButtonView.js: 231-250
     /Users/samreid/github/sun/js/buttons/RoundButtonView.js: 205-224

    - /Users/samreid/github/sun/js/buttons/RectangularButtonView.js: 293-318
     /Users/samreid/github/sun/js/buttons/RoundButtonView.js: 265-290

    - /Users/samreid/github/sun/js/buttons/RectangularButtonView.js: 339-413
     /Users/samreid/github/sun/js/buttons/RoundButtonView.js: 310-384

    - /Users/samreid/github/sun/js/buttons/RectangularButtonView.js: 436-466
     /Users/samreid/github/sun/js/buttons/RoundButtonView.js: 406-436

    - /Users/samreid/github/scenery-phet/js/NumberControl.js: 218-229
     /Users/samreid/github/sun/js/NumberSpinner.js: 265-276

    - /Users/samreid/github/griddle/js/griddle-config.js: 4-37
     /Users/samreid/github/sun/js/sun-config.js: 4-37

    - /Users/samreid/github/sun/js/ComboBox.js: 277-287
     /Users/samreid/github/sun/js/UIComponent.js: 71-81

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/trig-tour/js/trig-tour-config.js: 10-43

    - /Users/samreid/github/griddle/js/griddle-config.js: 4-32
     /Users/samreid/github/under-pressure/js/under-pressure-config.js: 4-32

    - /Users/samreid/github/area-model-multiplication/js/area-model-multiplication-config.js: 11-44
     /Users/samreid/github/unit-rates/js/unit-rates-config.js: 11-44

    - /Users/samreid/github/vegas/js/ScoreboardBar.js: 16-31
     /Users/samreid/github/vegas/js/ScoreboardPanel.js: 17-32

    - /Users/samreid/github/griddle/js/griddle-config.js: 4-33
     /Users/samreid/github/vegas/js/vegas-config.js: 4-33

    - /Users/samreid/github/expression-exchange/js/expression-exchange-config.js: 10-44
     /Users/samreid/github/vibe/js/vibe-config.js: 10-44

    - /Users/samreid/github/area-builder/js/area-builder-config.js: 10-40
     /Users/samreid/github/wave-on-a-string/js/wave-on-a-string-config.js: 12-42

 1.56% (5775 lines) duplicated lines out of 370417 total lines of code.

info:    Generate report time: durationMs=194
info:    All time: durationMs=89920
samreid commented 7 years ago

The yaml is a nice way to input the paths, but I can't get it to play nice with XSL or JSON. Perhaps next time.

samreid commented 7 years ago

I've got this working, lint is passing. Documentation is like this:

/**
 * Find code duplicates in the paths that are linted.  Usage:
 *
 * // To find duplicates in the repo itself
 * grunt find-duplicates
 *
 * // To find duplicates in the repo and its dependencies
 * grunt find-duplicates --all
 */

For example:

cd faradays-law
grunt find-duplicates

prints:

~/github/faradays-law$ grunt find-duplicates
Running "find-duplicates" task
info: Preprocessors running time: durationMs=13
info: Scanning 23 files for duplicates...
info: Scanning for duplicates time: durationMs=540
info: Scanning... done!

info: Start report generation...

info: Found 1 exact clones with 9 duplicated lines in 1 files

    - js/faradays-law/view/CoilsWiresNode.js: 43-52
     js/faradays-law/view/CoilsWiresNode.js: 58-67

 43 │ 58 │     ];                                                                                                         
 44 │ 59 │     this.addChild( new Path( new Shape()                                                                       
 45 │ 60 │       .moveTo( keyPoints[ 0 ].x, keyPoints[ 0 ].y )                                                            
 46 │ 61 │       .lineTo( keyPoints[ 1 ].x, keyPoints[ 1 ].y - ARC_RADIUS )                                               
 47 │ 62 │       .quadraticCurveTo( keyPoints[ 1 ].x, keyPoints[ 1 ].y, keyPoints[ 1 ].x + ARC_RADIUS, keyPoints[ 1 ].y ) 
 48 │ 63 │       .lineTo( keyPoints[ 2 ].x, keyPoints[ 2 ].y ), {                                                         
 49 │ 64 │       stroke: wireColor,                                                                                       
 50 │ 65 │       lineWidth: wireWidth                                                                                     
 51 │ 66 │     } ) );                                                                                                     
 52 │ 67 │                                                                                                                

 0.43% (9 lines) duplicated lines out of 2096 total lines of code.

warn: output file is not provided
info: Generate report time: durationMs=7

Done.

I'll commit, but I must notify other developers that after pulling, they have to run npm update in chipper before they can grunt again.

samreid commented 7 years ago

I emailed the team:

As part of https://github.com/phetsims/phet-info/issues/37#issuecomment-264665279 I added a grunt task that reports duplicated code. This added a new chipper package.json dependency, and hence when you pull, you must run npm update in chipper to be able to grunt again.

Best Regards, Sam

pixelzoom commented 7 years ago

This is great. But grunt find-duplicates identifies duplication within a repository, and (optionally) its dependencies. So it won't identify what seems to be the most typical duplication scenario. That is, copying code from one sim to another. That type of duplication (and specifically ColorProfile, https://github.com/phetsims/scenery-phet/issues/278#issuecomment-259536874) is what prompted this issue.

samreid commented 7 years ago

Thanks for your feedback, @pixelzoom. I added an --everything option that checks for duplicates across all PhET code. It catches (among other things) this duplicated code across area builder and balancing act. It took a few minutes to run on my machine.

    - ../area-builder/js/game/view/StartGameLevelNode.js: 59-84
     ../balancing-act/js/game/view/StartGameLevelNode.js: 55-80

 59 │ 55 │     }, options );                                                                                               
 60 │ 56 │                                                                                                                 
 61 │ 57 │     // Verify parameters                                                                                        
 62 │ 58 │     if ( iconNodes.length !== options.numLevels || scores.length !== options.numLevels ) {                      
 63 │ 59 │       throw new Error( 'Number of game levels doesn\'t match length of provided arrays' );                      
 64 │ 60 │     }                                                                                                           
 65 │ 61 │                                                                                                                 
 66 │ 62 │     // Title                                                                                                    
 67 │ 63 │     var title = new Text( options.titleString, { font: new PhetFont( 30 ), maxWidth: options.maxTitleWidth } ); 
 68 │ 64 │     this.addChild( title );                                                                                     
 69 │ 65 │                                                                                                                 
 70 │ 66 │     // Add the buttons                                                                                          
 71 │ 67 │     function createLevelStartFunction( level ) {                                                                
 72 │ 68 │       return function() { startLevelFunction( level ); };                                                       
 73 │ 69 │     }                                                                                                           
 74 │ 70 │                                                                                                                 
 75 │ 71 │     var buttons = new Array( options.numLevels );                                                               
 76 │ 72 │     for ( var i = 0; i < options.numLevels; i++ ) {                                                             
 77 │ 73 │       buttons[ i ] = new LevelSelectionButton(                                                                  
 78 │ 74 │         iconNodes[ i ],                                                                                         
 79 │ 75 │         options.numStarsOnButtons,                                                                              
 80 │ 76 │         createLevelStartFunction( i ),                                                                          
 81 │ 77 │         scores[ i ],                                                                                            
 82 │ 78 │         options.perfectScore,                                                                                   
 83 │ 79 │         {                                                                                                       
 84 │ 80 │           baseColor: options.buttonBackgroundColor   
samreid commented 7 years ago

In the above commit, I renamed the options to --dependencies and --everything.

samreid commented 7 years ago

Thinking through a process we could use to identify "newly copy-pasted" code, we could run grunt find-duplicates --everything > ../chipper/duplicates.txt and check that in. Then successive runs can be diffed easily.

samreid commented 7 years ago

I went with this path and I think I'll commit shortly:

grunt find-duplicates --everything > ../chipper/data/duplicates.txt
samreid commented 7 years ago

I tried adding duplicates with indenting, and they were not detected. We should see if this is a setting in the library we are using.

samreid commented 7 years ago

Even without the indents, the copy-paste was not detected. I'm not sure what the problem is.

samreid commented 7 years ago

I tried another "copy from on sim to another" and it was properly detected. My hypothesis is that an unparseable file wasn't checked for duplicates.

samreid commented 7 years ago

The test that failed before: copying lines 35-53 of ESPB BackgroundNode.js to hookes-law-main.js.

Testing again, I see no duplicates reported.

Adding vars and context so the file is parseable => the duplicate gets reported as:

    - ../energy-skate-park-basics/js/energy-skate-park-basics/view/BackgroundNode.js: 34-53
     ../hookes-law/js/hookes-law-main.js: 52-71

 34 │ 52 │     TandemNode.call( this, {                                       
 35 │ 53 │       pickable: false,                                             
 36 │ 54 │       tandem: tandem                                               
 37 │ 55 │     } );                                                           
 38 │ 56 │                                                                    
 39 │ 57 │     this.sky = new Rectangle( 0, 0, 0, 0 );                        
 40 │ 58 │     this.addChild( this.sky );                                     
 41 │ 59 │                                                                    
 42 │ 60 │     // Wait for bounds to fill in the grass                        
 43 │ 61 │     this.earth = new Rectangle( 0, 0, 0, 0, { fill: '#93774c' } ); 
 44 │ 62 │     this.addChild( this.earth );                                   
 45 │ 63 │                                                                    
 46 │ 64 │     this.cementY = layoutBounds.height - earthHeight;              
 47 │ 65 │                                                                    
 48 │ 66 │     this.mountain = new TandemImage( mountainImage, {              
 49 │ 67 │       bottom: this.cementY,                                        
 50 │ 68 │       tandem: tandem.createTandem( 'mountainImage' )               
 51 │ 69 │     } );                                                           
 52 │ 70 │     this.addChild( this.mountain );                                
 53 │ 71 │                                                                    

Case closed, it will only detect copy-pasted code that parses.

samreid commented 7 years ago

Now that we have a measure for detecting copy/pasted code, we can try to manage it. I'll close this issue and create a different one.