2828import java .util .logging .Logger ;
2929import java .util .regex .Matcher ;
3030import java .util .regex .Pattern ;
31+ import jdk .jshell .DeclarationSnippet ;
3132import jdk .jshell .Diag ;
3233import jdk .jshell .EvalException ;
3334import jdk .jshell .JShell ;
3435import jdk .jshell .JShellException ;
36+ import jdk .jshell .Snippet .SubKind ;
3537import jdk .jshell .SourceCodeAnalysis ;
3638import jdk .jshell .SnippetEvent ;
39+ import org .eclipse .lsp4j .MessageParams ;
40+ import org .eclipse .lsp4j .MessageType ;
3741import org .netbeans .modules .java .lsp .server .notebook .CellExecutionResult ;
3842import org .netbeans .modules .java .lsp .server .notebook .NotebookCellExecutionProgressResultParams ;
3943import org .netbeans .modules .java .lsp .server .notebook .NotebookCellExecutionProgressResultParams .Builder ;
4852 */
4953@ NbBundle .Messages ({
5054 "MSG_InterruptCodeCellExecSuccess=Code execution stopped successfully" ,
51- "MSG_InterruptCodeCellInfo=Code execution was interrupted"
55+ "MSG_InterruptCodeCellInfo=Code execution was interrupted" ,
56+ "MSG_NotebookRestartSession=Notebook session unavailable. Please restart the notebook kernel." ,
57+ "LBL_method=method" ,
58+ "LBL_variable=variable" ,
59+ "LBL_class=class" ,
60+ "LBL_enum=enum" ,
61+ "LBL_interface=interface" ,
62+ "LBL_annotation=annotation interface" ,
63+ "# {0} - declaration type and snippet name combination" ,
64+ "# {1} - unresolved dependencies list" ,
65+ "MSG_UnresolvedDepsRecoverableDefined=created {0}, however, it cannot be invoked or used or instantiated until {1} is declared" ,
66+ "# {0} - declaration type and snippet name combination" ,
67+ "# {1} - unresolved dependencies list" ,
68+ "MSG_UnresolvedDepsRecoverableNotDefined=created {0}, however, it cannot be referenced until {1} is declared" ,
69+ "MSG_ListCombine=, "
5270})
5371public class CodeEval {
5472
5573 private static final Logger LOG = Logger .getLogger (CodeEval .class .getName ());
5674 private static final String CODE_EXEC_INTERRUPT_SUCCESS_MESSAGE = Bundle .MSG_InterruptCodeCellExecSuccess ();
5775 private static final String CODE_EXEC_INTERRUPTED_MESSAGE = Bundle .MSG_InterruptCodeCellInfo ();
76+ private static final String RESTART_NOTEBOOK_SESSION_MESSAGE = Bundle .MSG_NotebookRestartSession ();
5877 private static final Pattern LINEBREAK = Pattern .compile ("\\ R" );
5978
6079 private final Map <String , RequestProcessor > codeExecMap = new ConcurrentHashMap <>();
@@ -146,6 +165,10 @@ public CompletableFuture<Boolean> evaluate(List<Object> arguments) {
146165 CompletableFuture <JShell > sessionFuture = NotebookSessionManager .getInstance ().getSessionFuture (notebookId );
147166 if (sessionFuture == null ) {
148167 LOG .warning ("notebook session not found" );
168+ NbCodeLanguageClient client = LanguageClientInstance .getInstance ().getClient ();
169+ if (client != null ) {
170+ client .showMessage (new MessageParams (MessageType .Error , RESTART_NOTEBOOK_SESSION_MESSAGE ));
171+ }
149172 return CompletableFuture .completedFuture (false );
150173 }
151174
@@ -218,16 +241,70 @@ private RequestProcessor getCodeExec(String notebookId) {
218241 return new RequestProcessor ("Jshell Code Evaluator for notebookId: " + id , 1 , true , true );
219242 });
220243 }
221-
222- private List <String > getCompilationErrors (JShell jshell , SnippetEvent event ) {
244+
245+ // Made package-private for easy unit test
246+ List <String > getCompilationErrors (JShell jshell , SnippetEvent event ) {
223247 List <String > compilationErrors = new ArrayList <>();
224248 jshell .diagnostics (event .snippet ()).forEach (diag -> {
225249 compilationErrors .addAll (displayableDiagnostic (event .snippet ().source (), diag ));
226250 });
227251
252+ if (event .snippet () instanceof DeclarationSnippet ) {
253+ DeclarationSnippet declSnippet = (DeclarationSnippet ) event .snippet ();
254+ List <String > unresolvedDeps = jshell .unresolvedDependencies (declSnippet ).toList ();
255+ if (!unresolvedDeps .isEmpty ()) {
256+ String msg = getUnresolvedDependencyMsg (event , declSnippet , unresolvedDeps );
257+ compilationErrors .add (msg );
258+ }
259+ }
260+
228261 return compilationErrors ;
229262 }
230263
264+ private String getUnresolvedDependencyMsg (SnippetEvent event , DeclarationSnippet snippet , List <String > unresolvedDeps ) {
265+ String declarationType = getDeclarationType (snippet );
266+ String prefix = declarationType .isEmpty ()
267+ ? snippet .name ()
268+ : declarationType + " " + snippet .name ();
269+
270+ String dependencies = String .join (Bundle .MSG_ListCombine (), unresolvedDeps );
271+
272+ if (event .status ().isDefined ()) {
273+ return Bundle .MSG_UnresolvedDepsRecoverableDefined (prefix , dependencies );
274+ }
275+ return Bundle .MSG_UnresolvedDepsRecoverableNotDefined (prefix , dependencies );
276+ }
277+
278+ private String getDeclarationType (DeclarationSnippet snippet ) {
279+ switch (snippet .kind ()) {
280+ case METHOD :
281+ return Bundle .LBL_method ();
282+ case VAR :
283+ return Bundle .LBL_variable ();
284+ case TYPE_DECL :
285+ return getTypeDeclarationType (snippet .subKind ());
286+ default :
287+ LOG .warning ("Cannot find declaration type of the snippet" );
288+ return "" ;
289+ }
290+ }
291+
292+ private String getTypeDeclarationType (SubKind subKind ) {
293+ switch (subKind ) {
294+ case CLASS_SUBKIND :
295+ return Bundle .LBL_class ();
296+ case INTERFACE_SUBKIND :
297+ return Bundle .LBL_interface ();
298+ case ENUM_SUBKIND :
299+ return Bundle .LBL_enum ();
300+ case ANNOTATION_TYPE_SUBKIND :
301+ return Bundle .LBL_annotation ();
302+ default :
303+ LOG .warning ("Cannot find declaration sub-type of the snippet" );
304+ return "" ;
305+ }
306+ }
307+
231308 private List <String > getRuntimeErrors (SnippetEvent event ) {
232309 List <String > runtimeErrors = new ArrayList <>();
233310 JShellException jshellException = event .exception ();
@@ -300,7 +377,7 @@ private StringBuilder correctExceptionName(StringBuilder output, int startIndex,
300377 }
301378 return output ;
302379 }
303-
380+
304381 private StringBuilder printStackTrace (StringBuilder output , Throwable exception ) {
305382 if (exception == null ) {
306383 return output != null ? output : new StringBuilder (0 );
@@ -311,17 +388,17 @@ private StringBuilder printStackTrace(StringBuilder output, Throwable exception)
311388 public void write (char [] cbuf , int off , int len ) {
312389 sb .append (cbuf , off , len );
313390 }
314-
391+
315392 @ Override
316393 public void flush () {
317394 }
318-
395+
319396 @ Override
320397 public void close () {
321398 }
322399 });
323400 exception .printStackTrace (stackWriter );
324-
401+
325402 return sb ;
326403 }
327404
0 commit comments