@@ -6,6 +6,8 @@ import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
66import { Tooltip } from "@mui/material" ;
77import Typography from "@mui/material/Typography" ;
88import Box from "@mui/material/Box" ;
9+ import { Link } from "@mui/material" ;
10+ import { List , ListItem } from "@mui/material" ;
911
1012function LinearProgressWithLabel ( props ) {
1113 return (
@@ -27,7 +29,67 @@ function LinearProgressWithLabel(props) {
2729 ) ;
2830}
2931
30- function AssignmentProblems ( { history, allProblemDisplayNames, numSolved } ) {
32+ const ProblemJumpLink = ( { lineNumber, editorRef, label } ) => {
33+ const handleJump = ( event ) => {
34+ // Prevent default link behavior if necessary
35+ event . preventDefault ( ) ;
36+
37+ const editorInstance = editorRef . current ;
38+ if ( ! editorInstance ) return ;
39+
40+ const targetEditor = editorInstance . getModifiedEditor
41+ ? editorInstance . getModifiedEditor ( )
42+ : editorInstance ;
43+
44+ targetEditor . revealLineInCenter ( lineNumber ) ;
45+ targetEditor . setPosition ( { lineNumber : lineNumber , column : 1 } ) ;
46+ targetEditor . focus ( ) ;
47+ } ;
48+
49+ return (
50+ < Link
51+ component = "button"
52+ variant = "body2"
53+ onClick = { handleJump }
54+ sx = { {
55+ textAlign : "left" ,
56+ verticalAlign : "baseline" ,
57+ textDecoration : "none" ,
58+ "&:hover" : {
59+ textDecoration : "underline" ,
60+ } ,
61+ cursor : "pointer" ,
62+ color : "primary.main" ,
63+ fontWeight : 500 ,
64+ } }
65+ >
66+ { label || `Line ${ lineNumber } ` }
67+ </ Link >
68+ ) ;
69+ } ;
70+
71+ function AssignmentProblems ( {
72+ history,
73+ allProblemDisplayNames,
74+ numSolved,
75+ editorRef,
76+ problemLines,
77+ } ) {
78+ function goToLine ( lineNumber ) {
79+ const editor = editorRef . current ;
80+ if ( ! editor ) return ;
81+
82+ // 1. Check if it's a Diff Editor (has getModifiedEditor method)
83+ // 2. Otherwise treat as a standard editor
84+ const targetEditor = editor . getModifiedEditor
85+ ? editor . getModifiedEditor ( )
86+ : editor ;
87+
88+ targetEditor . revealLineInCenter ( lineNumber ) ;
89+ targetEditor . setPosition ( { lineNumber : lineNumber , column : 1 } ) ;
90+ targetEditor . focus ( ) ;
91+ }
92+
3193 function getIcon ( problemDisplayName ) {
3294 const problemData = history . find (
3395 ( p ) => p . display_name === problemDisplayName ,
@@ -51,11 +113,72 @@ function AssignmentProblems({ history, allProblemDisplayNames, numSolved }) {
51113 return ( numSolved / allProblemDisplayNames . length ) * 100 ;
52114 }
53115
54- const problems = allProblemDisplayNames . map ( ( problemDisplayName ) => (
55- < div >
56- { getIcon ( problemDisplayName ) } { problemDisplayName }
57- </ div >
58- ) ) ;
116+ // TODO span styling and improve accessibility?
117+ const problems = allProblemDisplayNames . map ( ( problemDisplayName ) => {
118+ const lines = problemLines [ problemDisplayName ] ;
119+
120+ if ( problemLines [ problemDisplayName ] . length === 0 ) {
121+ return (
122+ < Box
123+ key = { problemDisplayName }
124+ sx = { { display : "flex" , alignItems : "center" , my : 0.5 } }
125+ >
126+ { getIcon ( problemDisplayName ) }
127+ < Typography variant = "body2" sx = { { ml : 1 , color : "text.disabled" } } >
128+ { problemDisplayName } (Not found)
129+ </ Typography >
130+ </ Box >
131+ ) ;
132+ } else if ( problemLines [ problemDisplayName ] . length === 1 ) {
133+ return (
134+ < Box
135+ key = { problemDisplayName }
136+ sx = { { display : "flex" , alignItems : "center" , my : 0.5 } }
137+ >
138+ { getIcon ( problemDisplayName ) }
139+ < Box sx = { { ml : 1 } } >
140+ < ProblemJumpLink
141+ lineNumber = { lines [ 0 ] }
142+ editorRef = { editorRef }
143+ label = { problemDisplayName }
144+ />
145+ </ Box >
146+ </ Box >
147+ ) ;
148+ } else {
149+ return (
150+ < Box key = { problemDisplayName } sx = { { my : 1 } } >
151+ < Box sx = { { display : "flex" , alignItems : "center" } } >
152+ { getIcon ( problemDisplayName ) }
153+ < Typography variant = "body2" sx = { { ml : 1 } } >
154+ { problemDisplayName }
155+ </ Typography >
156+ </ Box >
157+ < Box
158+ sx = { {
159+ pl : 4 ,
160+ display : "flex" ,
161+ flexDirection : "column" ,
162+ flexWrap : "wrap" ,
163+ } }
164+ >
165+ < List sx = { { listStyleType : "disc" , pl : "1rem" } } >
166+ { lines . map ( ( lineNumber ) => (
167+ < ListItem disablePadding sx = { { display : "list-item" } } >
168+ < ProblemJumpLink
169+ key = { `${ problemDisplayName } -${ lineNumber } ` }
170+ lineNumber = { lineNumber }
171+ editorRef = { editorRef }
172+ label = { `Line ${ lineNumber } ` }
173+ />
174+ </ ListItem >
175+ ) ) }
176+ </ List >
177+ </ Box >
178+ </ Box >
179+ ) ;
180+ }
181+ } ) ;
59182
60183 return (
61184 < div style = { { paddingTop : "1rem" , paddingBottom : "1rem" } } >
@@ -71,6 +194,8 @@ function Graphs({
71194 currBackupHistory,
72195 allProblemDisplayNames,
73196 selectedBackup,
197+ problemLines,
198+ editorRef,
74199} ) {
75200 return (
76201 < div >
@@ -79,6 +204,8 @@ function Graphs({
79204 history = { currBackupHistory }
80205 allProblemDisplayNames = { allProblemDisplayNames }
81206 numSolved = { numQuestionsSolved [ selectedBackup ] }
207+ problemLines = { problemLines }
208+ editorRef = { editorRef }
82209 />
83210 </ div >
84211 ) ;
0 commit comments