Different synchronization model for JShell, bugfixes for closing VM and prompt jshell_support_jdk9 jshell_api9_before
authorSvata Dedic <sdedic@netbeans.org>
Thu, 30 Jun 2016 14:20:56 +0200
branchjshell_support_jdk9
changeset 312318bc16e0113c0b
parent 312314 0ccfff7f8a62
child 312381 7ae3484ad7b7
Different synchronization model for JShell, bugfixes for closing VM and prompt
jshell.support/src/org/netbeans/modules/jshell/editor/OverrideEditorActions.java
jshell.support/src/org/netbeans/modules/jshell/env/JShellEnvironment.java
jshell.support/src/org/netbeans/modules/jshell/launch/DebugExecutionEnvironment.java
jshell.support/src/org/netbeans/modules/jshell/model/ConsoleModel.java
jshell.support/src/org/netbeans/modules/jshell/parsing/ConsoleEmbeddingProvider.java
jshell.support/src/org/netbeans/modules/jshell/parsing/ConsoleMainParser.java
jshell.support/src/org/netbeans/modules/jshell/parsing/EmbeddingProcessor.java
jshell.support/src/org/netbeans/modules/jshell/support/JShellLauncher.java
jshell.support/src/org/netbeans/modules/jshell/support/ShellSession.java
lib.nbjshell/src/org/netbeans/lib/nbjshell/NbExecutionControlBase.java
lib.nbjshell/src/org/netbeans/lib/nbjshell/RemoteExecutionSupport.java
     1.1 --- a/jshell.support/src/org/netbeans/modules/jshell/editor/OverrideEditorActions.java	Mon Jun 27 11:09:27 2016 +0200
     1.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/editor/OverrideEditorActions.java	Thu Jun 30 14:20:56 2016 +0200
     1.3 @@ -322,7 +322,7 @@
     1.4                  return;
     1.5              }
     1.6              ConsoleModel mod = session.getModel();
     1.7 -            ConsoleSection sec = mod.processInputSection();
     1.8 +            ConsoleSection sec = mod.processInputSection(false);
     1.9              // the interceptor is executed even if a break insertion fails, e.g. is
    1.10              // filtered out by DocumentFilter. Accept and process only those inserts,
    1.11              // which happen in the input section
    1.12 @@ -401,7 +401,7 @@
    1.13                  return;
    1.14              }
    1.15              ConsoleModel mod = s.getModel();
    1.16 -            ConsoleSection sec = mod.processInputSection();
    1.17 +            ConsoleSection sec = mod.processInputSection(false);
    1.18              if (sec == null || sec.isIncomplete()) {
    1.19                  return;
    1.20              }
     2.1 --- a/jshell.support/src/org/netbeans/modules/jshell/env/JShellEnvironment.java	Mon Jun 27 11:09:27 2016 +0200
     2.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/env/JShellEnvironment.java	Thu Jun 30 14:20:56 2016 +0200
     2.3 @@ -298,9 +298,16 @@
     2.4      }
     2.5      
     2.6      private void fireExecuting(ShellSession session, boolean start) {
     2.7 +        List<ShellListener> ll;
     2.8 +        synchronized (this) {
     2.9 +            ll = new ArrayList<>(this.shellListeners);
    2.10 +        }
    2.11 +        if (ll.isEmpty()) {
    2.12 +            return;
    2.13 +        }
    2.14          ShellEvent e = new ShellEvent(this, session, 
    2.15                  start ? ShellStatus.EXECUTE : ShellStatus.READY);
    2.16 -        shellListeners.stream().forEach(l -> l.shellStatusChanged(e));
    2.17 +        ll.stream().forEach(l -> l.shellStatusChanged(e));
    2.18      }
    2.19      
    2.20      private void doStartAndFire(ShellSession nss) {
     3.1 --- a/jshell.support/src/org/netbeans/modules/jshell/launch/DebugExecutionEnvironment.java	Mon Jun 27 11:09:27 2016 +0200
     3.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/launch/DebugExecutionEnvironment.java	Thu Jun 30 14:20:56 2016 +0200
     3.3 @@ -69,6 +69,7 @@
     3.4      @Override
     3.5      protected void shutdown() {
     3.6          agent.closeConnection(shellConnection);
     3.7 +        super.shutdown();
     3.8      }
     3.9  
    3.10      @Override
    3.11 @@ -216,6 +217,7 @@
    3.12              }
    3.13              closed = true;
    3.14          }
    3.15 +        shutdown();
    3.16  //        shellEnv.reportClosedBridge(reportSession, true);
    3.17      }
    3.18  
    3.19 @@ -227,6 +229,7 @@
    3.20              }
    3.21              closed = true;
    3.22          }
    3.23 +        shutdown();
    3.24  //        shellEnv.reportClosedBridge(reportSession, false);
    3.25      }
    3.26      
     4.1 --- a/jshell.support/src/org/netbeans/modules/jshell/model/ConsoleModel.java	Mon Jun 27 11:09:27 2016 +0200
     4.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/model/ConsoleModel.java	Thu Jun 30 14:20:56 2016 +0200
     4.3 @@ -70,6 +70,7 @@
     4.4  import org.netbeans.api.editor.document.AtomicLockDocument;
     4.5  import org.netbeans.api.editor.document.LineDocument;
     4.6  import org.netbeans.api.editor.document.LineDocumentUtils;
     4.7 +import org.netbeans.api.lexer.Language;
     4.8  import org.netbeans.api.lexer.TokenHierarchy;
     4.9  import org.netbeans.api.lexer.TokenSequence;
    4.10  import org.netbeans.editor.GuardedException;
    4.11 @@ -77,6 +78,7 @@
    4.12  import org.netbeans.lib.editor.util.swing.DocumentUtilities;
    4.13  import org.netbeans.modules.jshell.parsing.JShellParser;
    4.14  import org.netbeans.modules.jshell.parsing.ModelAccessor;
    4.15 +import org.netbeans.modules.parsing.api.Snapshot;
    4.16  import org.openide.util.Exceptions;
    4.17  import org.openide.util.RequestProcessor;
    4.18  import org.openide.util.Task;
    4.19 @@ -96,9 +98,9 @@
    4.20   * Use {@link #getInputEndOffset()} to get the current end offset or -1 if no active input section.
    4.21   * 
    4.22   * <p/>
    4.23 - * <b>Threading model:</b> all updates must be done in JShell evaluator thread. Synchronization on 
    4.24 - * the ConsoleModel <b>must be nested</b> in a document read-lock, if document access is necessary - doing
    4.25 - * the opposite will deadlock with EDT.
    4.26 + * <b>Threading model:</b> all updates must be done either in JSHell evaluator thread, or when the JShell evaluator
    4.27 + * thread does not evaluate user code. Calls from Parsing API may perform immediate updates provided that the
    4.28 + * evaluator does not evaluate user code.
    4.29   * 
    4.30   * @author sdedic
    4.31   */
    4.32 @@ -111,6 +113,8 @@
    4.33       */
    4.34      private JShell shell;
    4.35      
    4.36 +    private JShell privateShell;
    4.37 +    
    4.38      /**
    4.39       * The document for console contents
    4.40       */
    4.41 @@ -175,10 +179,10 @@
    4.42      }
    4.43      
    4.44      private int getScrollbackEnd() {
    4.45 -        ConsoleSection s = getInputSection();
    4.46          if (isExecute()) {
    4.47              return document.getLength();
    4.48          }
    4.49 +        ConsoleSection s = getInputSection();
    4.50          if (inputOffset == null) {
    4.51              return s != null ? s.getStart() : document.getLength();
    4.52          } else {
    4.53 @@ -231,6 +235,21 @@
    4.54      
    4.55      private RequestProcessor.Task inputTask;
    4.56      
    4.57 +    public void updateIfIdle() {
    4.58 +        synchronized (this) {
    4.59 +            if (isExecute()) {
    4.60 +                return;
    4.61 +            }
    4.62 +        }
    4.63 +        processInputSection(false);
    4.64 +    }
    4.65 +    
    4.66 +    public ConsoleSection   parseInputSection(Snapshot snap) {
    4.67 +        InputReader rdr = new InputReader(snap);
    4.68 +        rdr.run();
    4.69 +        return rdr.newSection;
    4.70 +    }
    4.71 +    
    4.72      /**
    4.73       * Returns the input section information. If the data is inaccurate,
    4.74       * tries to refresh them before returning from the call. The call may block,
    4.75 @@ -238,13 +257,13 @@
    4.76       * Document.
    4.77       * @return the input section
    4.78       */
    4.79 -    public ConsoleSection processInputSection() {
    4.80 +    public ConsoleSection processInputSection(boolean force) {
    4.81          synchronized (this) {
    4.82 -            if (inputValid) {
    4.83 +            if (!shouldRefresh() && !force) {
    4.84                  return getInputSection();
    4.85              }
    4.86          }
    4.87 -        refreshInput(true).waitFinished();
    4.88 +        refreshInput(force, true).waitFinished();
    4.89          return inputSection;
    4.90      }
    4.91      
    4.92 @@ -263,29 +282,57 @@
    4.93                  return null;
    4.94              }
    4.95          } 
    4.96 -        if (!inputValid) {
    4.97 +        if (shouldRefresh()) {
    4.98              // in evaluator, the refresh happens immediately
    4.99 -            refreshInput(false);
   4.100 +            refreshInput(false, false);
   4.101          }
   4.102          return inputSection;
   4.103      }
   4.104      
   4.105 -    private Task refreshInput(boolean now) {
   4.106 +    /**
   4.107 +     * True, if the thread itself is refreshing the model.
   4.108 +     */
   4.109 +    private boolean isRefreshPending() {
   4.110 +        return refreshPending.get();
   4.111 +    }
   4.112 +    
   4.113 +    private boolean isInputValid() {
   4.114 +        return inputValid;
   4.115 +    }
   4.116 +    
   4.117 +    private synchronized boolean shouldRefresh() {
   4.118 +        return !inputValid && inputTask == null;
   4.119 +    }
   4.120 +    
   4.121 +    private Task refreshInput(boolean force, boolean now) {
   4.122          Task t;
   4.123          boolean wait;
   4.124 -        
   4.125          synchronized (this) {
   4.126 -             if (executing) {
   4.127 -                return Task.EMPTY;
   4.128 +            boolean rp = isRefreshPending();
   4.129 +            if (rp || executing) {
   4.130 +                 // cannot refresh during execution. Must not refresh if the
   4.131 +                 // thread itself is the refresh one.
   4.132 +                 return Task.EMPTY;
   4.133              }
   4.134 +            // reset the valid flag
   4.135              inputValid = false;
   4.136 +            boolean sched = inputTask == null;
   4.137              if (inputTask != null) {
   4.138 -                inputTask.schedule(now ? 0 : 200);
   4.139 -            } else {
   4.140 -                inputTask = evaluator.post(new InputReader(), now ? 0 : 200);
   4.141 +                if (!force) {
   4.142 +                    return inputTask;
   4.143 +                }
   4.144 +                if (inputTask.cancel()) {
   4.145 +                    inputTask.schedule(now ? 0 : 200);
   4.146 +                    sched = true;
   4.147 +                }
   4.148 +            }
   4.149 +            if (sched) {
   4.150 +                InputReader r = new InputReader();
   4.151 +                inputTask = evaluator.post(r, now ? 0 : 200);
   4.152 +                r.myTask = inputTask;
   4.153              }
   4.154              t = inputTask;
   4.155 -            wait = !refreshPending && evaluator.isRequestProcessorThread();
   4.156 +            wait = now && !rp && evaluator.isRequestProcessorThread();
   4.157          }
   4.158          if (wait) {
   4.159              t.waitFinished();
   4.160 @@ -293,12 +340,23 @@
   4.161          return t;
   4.162      }
   4.163      
   4.164 +    private synchronized void clearInputTask(Task t) {
   4.165 +        if (t == inputTask) {
   4.166 +            inputTask = null;
   4.167 +        }
   4.168 +    }
   4.169 +    
   4.170      /**
   4.171       * If true, the refresh task is running. Input section is then to be returned
   4.172       * immediately, no wait on the refresh task to prevent self-deadlock.
   4.173       * Set and reset by the InputReader only.
   4.174       */
   4.175 -    private volatile boolean refreshPending;
   4.176 +    private ThreadLocal<Boolean> refreshPending = new ThreadLocal<Boolean>() {
   4.177 +        @Override
   4.178 +        protected Boolean initialValue() {
   4.179 +            return false;
   4.180 +        }
   4.181 +    };
   4.182      
   4.183      private class InputReader extends EventBuffer implements Runnable {
   4.184          private int stage;
   4.185 @@ -309,9 +367,18 @@
   4.186          private long endSerial;
   4.187          private Position endPos;
   4.188          private int stalledInput;
   4.189 +        private Task myTask;
   4.190 +        private Snapshot processSnapshot;
   4.191          
   4.192          private List<ConsoleSection>    updateSections;
   4.193          
   4.194 +        private InputReader(Snapshot snapshot) {
   4.195 +            this.processSnapshot = snapshot;
   4.196 +        }
   4.197 +        
   4.198 +        private InputReader() {
   4.199 +        }
   4.200 +        
   4.201          @Override
   4.202          public void run() {
   4.203              switch (stage++) {
   4.204 @@ -320,10 +387,14 @@
   4.205                      doIt();
   4.206                      break;
   4.207                  case 1:
   4.208 -                    readContents();
   4.209 -                    if (contents != null) {
   4.210 -                        parseInput();
   4.211 -                        propagateResults();
   4.212 +                    try {
   4.213 +                        readContents();
   4.214 +                        if (contents != null) {
   4.215 +                            parseInput();
   4.216 +                            propagateResults();
   4.217 +                        }
   4.218 +                    } finally {
   4.219 +                        clearInputTask(myTask);
   4.220                      }
   4.221                      break;
   4.222              }
   4.223 @@ -332,14 +403,19 @@
   4.224          public void doIt() {
   4.225              // stage 0, read the document and serial
   4.226              synchronized (ConsoleModel.this) {
   4.227 -                refreshPending = true;
   4.228 +                refreshPending.set(true);
   4.229              }
   4.230              try {
   4.231                  // stage 1
   4.232 -                document.render(this);
   4.233 +                if (processSnapshot != null) {
   4.234 +                    // do not use document lock, get the contents from the snapshot.
   4.235 +                    run();
   4.236 +                } else {
   4.237 +                    document.render(this);
   4.238 +                }
   4.239              } finally {
   4.240                  synchronized (ConsoleModel.this) {
   4.241 -                    refreshPending = false;
   4.242 +                    refreshPending.set(false);
   4.243                  }
   4.244                  stage = 0;
   4.245              }
   4.246 @@ -347,11 +423,11 @@
   4.247          
   4.248          private void readContents() {
   4.249              stalledInput = getInputOffset();
   4.250 -            if (isExecute() || stalledInput == -1) {
   4.251 +            if (isExecute() /* || stalledInput == -1 */) {
   4.252                  return;
   4.253              }
   4.254              int is = getScrollbackEnd();
   4.255 -            if (stalledInput < is) {
   4.256 +            if (stalledInput >= 0 && stalledInput < is) {
   4.257                  inputStart = lastSection != null ? lastSection.getStart() : stalledInput;
   4.258                  LOG.log(Level.FINER, "Detected stale input. Know input at {0} while anchor moved to {1}. LastSection = {2}, inputStart = {3}", new Object[] {
   4.259                      stalledInput, is, lastSection, inputStart
   4.260 @@ -360,10 +436,15 @@
   4.261                  inputStart = is;
   4.262              }
   4.263              try {
   4.264 -                contents = DocumentUtilities.getText(document, inputStart, document.getLength() - inputStart);
   4.265 +                if (processSnapshot != null) {
   4.266 +                    contents = processSnapshot.getText().subSequence(inputStart,processSnapshot.getText().length()).toString();
   4.267 +                    // intentionally do not fetch document's serial. the results will not update at the end
   4.268 +                } else {
   4.269 +                    contents = DocumentUtilities.getText(document, inputStart, document.getLength() - inputStart);
   4.270 +                    docSerial = DocumentUtilities.getDocumentVersion(document);
   4.271 +                }
   4.272              } catch (BadLocationException ex) {
   4.273              }
   4.274 -            docSerial = DocumentUtilities.getDocumentVersion(document);
   4.275          }
   4.276          
   4.277          private void getPositionAndSerial() {
   4.278 @@ -392,13 +473,17 @@
   4.279              
   4.280              inputEndPos = endPos;
   4.281              inputValid = true;
   4.282 -            try {
   4.283 -                inputOffset = document.createPosition(newSection.getStart(), Position.Bias.Forward);
   4.284 -            } catch (BadLocationException ex) {
   4.285 -                // should not happen, running inside readlock.
   4.286 +            if (newSection != null) {
   4.287 +                try {
   4.288 +                    inputOffset = document.createPosition(newSection.getStart(), Position.Bias.Forward);
   4.289 +                } catch (BadLocationException ex) {
   4.290 +                    // should not happen, running inside readlock.
   4.291 +                }
   4.292              }
   4.293 -            for (ConsoleSection s : updateSections) {
   4.294 -                addOrUpdate(s);
   4.295 +            if (updateSections != null) {
   4.296 +                for (ConsoleSection s : updateSections) {
   4.297 +                    addOrUpdate(s);
   4.298 +                }
   4.299              }
   4.300          }
   4.301  /*
   4.302 @@ -463,11 +548,25 @@
   4.303          }
   4.304          */
   4.305          private void parseInput() {
   4.306 -            TokenHierarchy th = TokenHierarchy.get(getDocument());
   4.307 -            TokenSequence seq = th.tokenSequence();
   4.308 -            assert seq != null;
   4.309 -            seq.move(inputStart);
   4.310 -            JShellParser parser2 = new JShellParser(shell, seq, 0, inputStart + contents.length());
   4.311 +            TokenHierarchy th;
   4.312 +            TokenSequence seq;
   4.313 +            int limit = contents.length();
   4.314 +            
   4.315 +            if (processSnapshot == null) {
   4.316 +                th = TokenHierarchy.get(getDocument()); 
   4.317 +                seq = th.tokenSequence();
   4.318 +                seq.move(inputStart);
   4.319 +                limit += inputStart;
   4.320 +            } else {
   4.321 +                th = TokenHierarchy.create(contents, Language.find("text/x-repl"));
   4.322 +                seq = th.tokenSequence();
   4.323 +                seq.move(0);
   4.324 +            }
   4.325 +            
   4.326 +            JShellParser parser2 = new JShellParser(
   4.327 +                    (evaluator.isRequestProcessorThread() || !isExecute())  ? 
   4.328 +                            shell : 
   4.329 +                            createPrivateShell(), seq, 0, limit);
   4.330              
   4.331              parser2.execute();
   4.332              
   4.333 @@ -532,10 +631,6 @@
   4.334          }
   4.335      }
   4.336      
   4.337 -    public void inputChanged() {
   4.338 -        refreshInput(false);
   4.339 -    }
   4.340 -    
   4.341      private static final RequestProcessor RP = new RequestProcessor(ConsoleModel.class);
   4.342      
   4.343      private void notifyUpdated(ConsoleSection s) {
   4.344 @@ -701,75 +796,6 @@
   4.345              return;
   4.346          }
   4.347          
   4.348 -        List<ConsoleSection> created = new ArrayList<>(sections);
   4.349 -        ConsoleSection updated = null;
   4.350 -
   4.351 -        /*
   4.352 -        ConsoleSection l = sections.get(sections.size() - 1);
   4.353 -        ConsoleSection f = sections.get(0);
   4.354 -        
   4.355 -        synchronized (this) {
   4.356 -            if (lastSection != null) {
   4.357 -                if (sections.size() > 1) {
   4.358 -                    if (lastSection != last) {
   4.359 -                        // no update
   4.360 -                        scrollbackSections.add(lastSection);
   4.361 -                    } else {
   4.362 -                        updated = created.remove(0);
   4.363 -                    }
   4.364 -                    scrollbackSections.addAll(sections.subList(0, sections.size() - 1));
   4.365 -                } else {
   4.366 -                    // just 1 section updated, it is the last section.
   4.367 -                    if (last != null && lastSection == last) {
   4.368 -                        lastSection = updated = f;
   4.369 -                        created.clear();
   4.370 -                    } else {
   4.371 -                        scrollbackSections.add(lastSection);
   4.372 -                    }
   4.373 -                }
   4.374 -            } else {
   4.375 -                scrollbackSections.addAll(sections.subList(0, sections.size() - 1));
   4.376 -            }
   4.377 -            if (l.getType().input) {
   4.378 -                if (sections.size() > 1) {
   4.379 -                    ConsoleSection prevLast = sections.get(sections.size() - 2);
   4.380 -                    if (!prevLast.getType().input) {
   4.381 -                        lastSection = prevLast;
   4.382 -                    } else {
   4.383 -                        lastSection = null;
   4.384 -                    }
   4.385 -                } else {
   4.386 -                    lastSection = null;
   4.387 -                }
   4.388 -            } else {
   4.389 -                lastSection = l;
   4.390 -            }
   4.391 -            processed = end;
   4.392 -        }
   4.393 -        ConsoleSection execSection;
   4.394 -        
   4.395 -        if (l.getType().input) {
   4.396 -            // prompt was displayed
   4.397 -            execSection = executingSection;
   4.398 -            setInputSection(l);
   4.399 -        } else {
   4.400 -            lastSection = l;
   4.401 -            execSection = null;
   4.402 -        } 
   4.403 -        final ConsoleSection fu = updated;
   4.404 -        RP.post(() -> {
   4.405 -            if (fu != null) {
   4.406 -                notifyUpdated(fu);
   4.407 -            }
   4.408 -            if (!created.isEmpty()) {
   4.409 -                notifyCreated(created);
   4.410 -            }
   4.411 -            if (execSection != null) {
   4.412 -                ConsoleEvent e = new ConsoleEvent(this, execSection, false);
   4.413 -                listeners.stream().forEach(t -> t.executing(e));
   4.414 -            }
   4.415 -        });
   4.416 -        */
   4.417          new EventBuffer() {
   4.418              @Override
   4.419              protected void doUpdates() {
   4.420 @@ -801,7 +827,7 @@
   4.421              }
   4.422              textAppended(document.getLength() + 1);
   4.423          } else if (inputSection != null && inputSection.getStart() < s) {
   4.424 -            refreshInput(false);
   4.425 +            refreshInput(true, false);
   4.426          }
   4.427      }
   4.428      
   4.429 @@ -837,28 +863,48 @@
   4.430       * input section to the scrollback
   4.431       * 
   4.432       */
   4.433 -    synchronized void beforeExecution() {
   4.434 -        assert !isExecute();
   4.435 -        ConsoleSection is = getInputSection();
   4.436 -        executingSection = is;
   4.437 -        if (is != null) {
   4.438 -            // the input will be added to the scrollback; if something is still
   4.439 -            // buffered in the lastSection, add it first:
   4.440 -            if (lastSection != null) {
   4.441 -                scrollbackSections.add(lastSection);
   4.442 +    void beforeExecution() {
   4.443 +        Task t = null;
   4.444 +        ConsoleSection is = null;
   4.445 +        while (true) {
   4.446 +            // wait after all refreshes are complete, block furthe refreshes by setting up executing flag
   4.447 +            synchronized (this) {
   4.448 +                assert !isExecute();
   4.449 +                t = inputTask;
   4.450              }
   4.451 -            lastSection = null;
   4.452 -            scrollbackSections.add(executingSection);
   4.453 +            if (t != null) {
   4.454 +                t.waitFinished();
   4.455 +            }
   4.456 +            is = getInputSection();
   4.457 +            synchronized (this) {
   4.458 +                if (inputTask == null) {
   4.459 +                    // no refresh is pending, change mode
   4.460 +                    executingSection = is;
   4.461 +                    executing = true;
   4.462 +                    break;
   4.463 +                }
   4.464 +            }
   4.465          }
   4.466 -        executing = true;
   4.467 -        if (is != null) {
   4.468 -            RP.post(() -> { 
   4.469 -                // notify that the scrollback has been changed.
   4.470 -                notifyUpdated(is); 
   4.471 -                
   4.472 -                ConsoleEvent e = new ConsoleEvent(this, is, true);
   4.473 -                listeners.stream().forEach(l -> l.executing(e));
   4.474 -            });
   4.475 +        synchronized (this) {
   4.476 +            ConsoleSection finIs = is;
   4.477 +            if (finIs != null) {
   4.478 +                // the input will be added to the scrollback; if something is still
   4.479 +                // buffered in the lastSection, add it first:
   4.480 +                if (lastSection != null) {
   4.481 +                    scrollbackSections.add(lastSection);
   4.482 +                }
   4.483 +                lastSection = null;
   4.484 +                scrollbackSections.add(executingSection);
   4.485 +            }
   4.486 +            if (is != null) {
   4.487 +                RP.post(() -> { 
   4.488 +                    // notify that the scrollback has been changed.
   4.489 +                    notifyUpdated(finIs); 
   4.490 +
   4.491 +                    ConsoleEvent e = new ConsoleEvent(this, finIs, true);
   4.492 +                    listeners.stream().forEach(l -> l.executing(e));
   4.493 +                });
   4.494 +            }
   4.495          }
   4.496      }
   4.497      
   4.498 @@ -890,6 +936,13 @@
   4.499          this.shell = shell;
   4.500          snippetSubscription = shell.onSnippetEvent(this::acceptSnippet);
   4.501      }
   4.502 +    
   4.503 +    private synchronized JShell createPrivateShell() {
   4.504 +        if (privateShell == null) {
   4.505 +            privateShell = JShell.create();
   4.506 +        }
   4.507 +        return privateShell;
   4.508 +    }
   4.509  
   4.510      /**
   4.511       * Provides mapping between snippets and individual input sections
   4.512 @@ -1027,7 +1080,7 @@
   4.513  
   4.514          @Override
   4.515          public void removeUpdate(DocumentEvent e) {
   4.516 -            change(e);
   4.517 +            //change(e);
   4.518          }
   4.519  
   4.520          @Override
   4.521 @@ -1305,6 +1358,8 @@
   4.522          return shell;
   4.523      }
   4.524      
   4.525 +    private Task execWaitTask = null;
   4.526 +    
   4.527      static class ModelAccImpl extends ModelAccessor {
   4.528  
   4.529          @Override
   4.530 @@ -1380,7 +1435,7 @@
   4.531      }
   4.532  
   4.533      void ensureInputSectionAvailable(Supplier<String> promptSupplier) {
   4.534 -        ConsoleSection s = processInputSection();
   4.535 +        ConsoleSection s = processInputSection(true);
   4.536          if (s != null) {
   4.537              return;
   4.538          }
     5.1 --- a/jshell.support/src/org/netbeans/modules/jshell/parsing/ConsoleEmbeddingProvider.java	Mon Jun 27 11:09:27 2016 +0200
     5.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/parsing/ConsoleEmbeddingProvider.java	Thu Jun 30 14:20:56 2016 +0200
     5.3 @@ -45,6 +45,7 @@
     5.4  import java.util.Collections;
     5.5  import java.util.List;
     5.6  import javax.swing.text.Document;
     5.7 +import org.netbeans.modules.jshell.model.ConsoleSection;
     5.8  import org.netbeans.modules.jshell.support.ShellSession;
     5.9  import org.netbeans.modules.parsing.api.Embedding;
    5.10  import org.netbeans.modules.parsing.api.Snapshot;
    5.11 @@ -77,7 +78,8 @@
    5.12          if (model == null) {
    5.13              return Collections.emptyList();
    5.14          }
    5.15 -        EmbeddingProcessor p = new EmbeddingProcessor(session, model, snapshot);
    5.16 +        ConsoleSection inputSection = model.parseInputSection(snapshot);
    5.17 +        EmbeddingProcessor p = new EmbeddingProcessor(session, model, snapshot, inputSection);
    5.18          return p.process();
    5.19      }
    5.20      
     6.1 --- a/jshell.support/src/org/netbeans/modules/jshell/parsing/ConsoleMainParser.java	Mon Jun 27 11:09:27 2016 +0200
     6.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/parsing/ConsoleMainParser.java	Thu Jun 30 14:20:56 2016 +0200
     6.3 @@ -45,6 +45,7 @@
     6.4  import org.netbeans.modules.jshell.model.ConsoleResult;
     6.5  import java.util.Collection;
     6.6  import javax.swing.event.ChangeListener;
     6.7 +import javax.swing.text.BadLocationException;
     6.8  import javax.swing.text.Document;
     6.9  import org.netbeans.api.editor.mimelookup.MimeRegistration;
    6.10  import org.netbeans.modules.jshell.support.ShellSession;
    6.11 @@ -54,6 +55,7 @@
    6.12  import org.netbeans.modules.parsing.spi.Parser;
    6.13  import org.netbeans.modules.parsing.spi.ParserFactory;
    6.14  import org.netbeans.modules.parsing.spi.SourceModificationEvent;
    6.15 +import org.openide.util.Exceptions;
    6.16  
    6.17  /**
    6.18   *
    6.19 @@ -71,6 +73,7 @@
    6.20          ConsoleModel theModel = null;
    6.21          if (ss != null) {
    6.22              theModel = ss.getModel();
    6.23 +            theModel.updateIfIdle();
    6.24          }
    6.25          this.result = new ConsoleResult(theModel, snapshot);
    6.26      }
     7.1 --- a/jshell.support/src/org/netbeans/modules/jshell/parsing/EmbeddingProcessor.java	Mon Jun 27 11:09:27 2016 +0200
     7.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/parsing/EmbeddingProcessor.java	Thu Jun 30 14:20:56 2016 +0200
     7.3 @@ -72,6 +72,8 @@
     7.4      private final JShell shell;
     7.5      private final Snapshot snapshot;
     7.6      private final ShellSession session;
     7.7 +    private final ConsoleSection inputSection;
     7.8 +    private final ConsoleSection modelInputSection;
     7.9      
    7.10      private StringBuilder precedingImports = new StringBuilder();
    7.11      
    7.12 @@ -79,12 +81,14 @@
    7.13      
    7.14      private int snippetIndex;
    7.15  
    7.16 -    public EmbeddingProcessor(ShellSession session, ConsoleModel model, Snapshot snapshot) {
    7.17 +    public EmbeddingProcessor(ShellSession session, ConsoleModel model, Snapshot snapshot, ConsoleSection snapshotInput) {
    7.18          this.session = session;
    7.19          this.model = model;
    7.20          this.snapshot = snapshot;
    7.21          
    7.22          this.shell = model.getShell();
    7.23 +        this.modelInputSection = model.getInputSection();
    7.24 +        this.inputSection = snapshotInput != null ? snapshotInput : modelInputSection;
    7.25      }
    7.26      
    7.27      @SuppressWarnings("ReturnOfCollectionOrArrayField")
    7.28 @@ -121,7 +125,7 @@
    7.29          if (snipFile == null) {
    7.30              return;
    7.31          }
    7.32 -        ConsoleSection activeInput = model.getInputSection();
    7.33 +        ConsoleSection activeInput = inputSection;
    7.34          
    7.35          String prologText = contents.substring(0, ts);
    7.36          
     8.1 --- a/jshell.support/src/org/netbeans/modules/jshell/support/JShellLauncher.java	Mon Jun 27 11:09:27 2016 +0200
     8.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/support/JShellLauncher.java	Thu Jun 30 14:20:56 2016 +0200
     8.3 @@ -100,13 +100,15 @@
     8.4          closeState();
     8.5      }
     8.6      
     8.7 -    public void evaluate(String command) throws IOException {
     8.8 +    public void evaluate(String command, boolean prompt) throws IOException {
     8.9          ensureLive();
    8.10          String trimmed = trimEnd(command);
    8.11          if (!trimmed.isEmpty()) {
    8.12              prefix = process(prefix, command);
    8.13          }
    8.14 -//        cmdout.append(prompt(!prefix.isEmpty()));
    8.15 +        if (prompt) {
    8.16 +            cmdout.append(prompt(!prefix.isEmpty()));
    8.17 +        }
    8.18      }
    8.19      
    8.20      public List<String> completion(String command) {
     9.1 --- a/jshell.support/src/org/netbeans/modules/jshell/support/ShellSession.java	Mon Jun 27 11:09:27 2016 +0200
     9.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/support/ShellSession.java	Thu Jun 30 14:20:56 2016 +0200
     9.3 @@ -576,13 +576,14 @@
     9.4          synchronized (allSessions) {
     9.5              allSessions.put(consoleDocument, new WeakReference<>(this));
     9.6          }
     9.7 +        GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, new ClassPath[] { 
     9.8 +            env.getSnippetClassPath()
     9.9 +        });
    9.10 +
    9.11 +        URL url = URLMapper.findURL(workRoot, URLMapper.INTERNAL);
    9.12 +        IndexingManager.getDefault().refreshIndexAndWait(url, null, true);
    9.13          return Pair.of(previous, evaluator.post(() -> {
    9.14 -            GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, new ClassPath[] { 
    9.15 -                env.getSnippetClassPath()
    9.16 -            });
    9.17  
    9.18 -            URL url = URLMapper.findURL(workRoot, URLMapper.INTERNAL);
    9.19 -            IndexingManager.getDefault().refreshIndexAndWait(url, null, true);
    9.20              ModelAccessor.INSTANCE.execute(model, () -> {
    9.21                  try {
    9.22                      getJShell();
    9.23 @@ -724,7 +725,7 @@
    9.24      }
    9.25  
    9.26      private void ensureInputSectionAvailable() {
    9.27 -        ConsoleSection s = model.processInputSection();
    9.28 +        ConsoleSection s = model.processInputSection(false);
    9.29          if (s != null) {
    9.30              return;
    9.31          }
    9.32 @@ -1014,7 +1015,7 @@
    9.33          post(() -> {
    9.34              String c = command;
    9.35              if (c == null) {
    9.36 -                ConsoleSection s = model.processInputSection();
    9.37 +                ConsoleSection s = model.processInputSection(true);
    9.38                  if (s == null) {
    9.39                      return;
    9.40                  }
    9.41 @@ -1040,7 +1041,7 @@
    9.42          "MSG_ErrorExecutingCommand=Note: You may need to restart the Java Shell to resume proper operation"
    9.43      })
    9.44      private void doExecuteCommands() {
    9.45 -        ConsoleSection sec = model.processInputSection();
    9.46 +        ConsoleSection sec = model.processInputSection(true);
    9.47          if (sec == null) {
    9.48              return;
    9.49          }
    9.50 @@ -1069,7 +1070,7 @@
    9.51                  try {
    9.52                      for (String s : toExec) {
    9.53                              ModelAccessor.INSTANCE.setSnippetOffset(model, exec.offsetToContents(ranges[index].start, true));
    9.54 -                            launcher.evaluate(s);
    9.55 +                            launcher.evaluate(s, index == toExec.size() - 1);
    9.56                              if (erroneous) {
    9.57                                  break;
    9.58                              }
    9.59 @@ -1080,6 +1081,7 @@
    9.60                      reportShellMessage(Bundle.MSG_ErrorExecutingCommand());
    9.61                  }
    9.62              } finally {
    9.63 +                System.err.println("Bubu");
    9.64                  erroneous = false;
    9.65              }
    9.66          }, this::getPromptAfterError);
    10.1 --- a/lib.nbjshell/src/org/netbeans/lib/nbjshell/NbExecutionControlBase.java	Mon Jun 27 11:09:27 2016 +0200
    10.2 +++ b/lib.nbjshell/src/org/netbeans/lib/nbjshell/NbExecutionControlBase.java	Thu Jun 30 14:20:56 2016 +0200
    10.3 @@ -109,8 +109,8 @@
    10.4          try {
    10.5              delegate.close();
    10.6          } catch (IOException ex) {
    10.7 -            
    10.8          }
    10.9 +        shutdown();
   10.10      }
   10.11  
   10.12      public boolean load(Collection<String> classes) {
   10.13 @@ -140,6 +140,9 @@
   10.14      protected abstract boolean isClosed();
   10.15      
   10.16      protected void shutdown() {
   10.17 +        if (remoteIn == null) {
   10.18 +            return;
   10.19 +        }
   10.20          try {
   10.21              remoteIn.close();
   10.22              remoteOut.close();
    11.1 --- a/lib.nbjshell/src/org/netbeans/lib/nbjshell/RemoteExecutionSupport.java	Mon Jun 27 11:09:27 2016 +0200
    11.2 +++ b/lib.nbjshell/src/org/netbeans/lib/nbjshell/RemoteExecutionSupport.java	Thu Jun 30 14:20:56 2016 +0200
    11.3 @@ -507,6 +507,7 @@
    11.4          final DemultiplexInput demux[] = new DemultiplexInput[1];
    11.5          PipeInputStream pis = new PipeInputStream() {
    11.6              public void close() {
    11.7 +                super.close();
    11.8                  demux[0].close();
    11.9              }
   11.10          };