static int nextTokenType(CeylonParseController cpc, CommonToken token) {
   for (int i = token.getTokenIndex() + 1; i < cpc.getTokens().size(); i++) {
     CommonToken tok = cpc.getTokens().get(i);
     if (tok.getChannel() != CommonToken.HIDDEN_CHANNEL) {
       return tok.getType();
     }
   }
   return -1;
 }
  private boolean terminateWithSemicolon() throws Exception {
    final IDocument doc = editor.getCeylonSourceViewer().getDocument();
    final TextChange change = new DocumentChange("Terminate Statement", doc);
    change.setEdit(new MultiTextEdit());
    CeylonParseController parser = parse();
    CompilationUnit rootNode = parser.getRootNode();
    IRegion li = getLineInfo(doc);
    String lineText = doc.get(li.getOffset(), li.getLength());
    final List<CommonToken> tokens = parser.getTokens();
    // final int startOfCodeInLine = getCodeStart(li, lineText, tokens);
    final int endOfCodeInLine = getCodeEnd(li, lineText, tokens);
    if (!doc.get(endOfCodeInLine, 1).equals(";")) {
      new Processor() {
        @Override
        public void visit(Tree.Annotation that) {
          super.visit(that);
          terminateWithSemicolon(that);
        }

        @Override
        public void visit(Tree.StatementOrArgument that) {
          super.visit(that);
          if (that instanceof Tree.ExecutableStatement && !(that instanceof Tree.ControlStatement)
              || that instanceof Tree.AttributeDeclaration
              || that instanceof Tree.MethodDeclaration
              || that instanceof Tree.ClassDeclaration
              || that instanceof Tree.InterfaceDeclaration
              || that instanceof Tree.SpecifiedArgument) {
            terminateWithSemicolon(that);
          }
        }

        private void terminateWithSemicolon(Node that) {
          try {
            if (that.getStartIndex() <= endOfCodeInLine && that.getStopIndex() >= endOfCodeInLine) {
              Token et = that.getEndToken();
              if (et == null
                  || et.getType() != CeylonLexer.SEMICOLON
                  || that.getStopIndex() > endOfCodeInLine) {
                if (!change.getEdit().hasChildren()) {
                  change.addEdit(new InsertEdit(endOfCodeInLine + 1, ";"));
                }
              }
            }
          } catch (Exception e) {
            e.printStackTrace();
          }
        }
      }.visit(rootNode);
      if (change.getEdit().hasChildren()) {
        change.perform(new NullProgressMonitor());
        return true;
      }
    }
    return false;
  }
 @Override
 public IHyperlink[] detectHyperlinks(
     ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) {
   if (controller == null || controller.getLastCompilationUnit() == null) {
     return null;
   } else {
     Node node =
         findNode(
             controller.getLastCompilationUnit(),
             controller.getTokens(),
             region.getOffset(),
             region.getOffset() + region.getLength());
     if (node == null) {
       return null;
     } else {
       Node id = getIdentifyingNode(node);
       if (id == null) {
         return null;
       } else {
         Referenceable referenceable = getReferencedModel(node);
         Backends supportedBackends = supportedBackends();
         if (referenceable instanceof Declaration) {
           Declaration dec = (Declaration) referenceable;
           if (dec.isNative()) {
             if (supportedBackends.none()) {
               return null;
             } else {
               referenceable = resolveNative(referenceable, dec, supportedBackends);
             }
           } else {
             if (!supportedBackends.none()) {
               return null;
             }
           }
         } else { // Module or package descriptors
           if (!supportedBackends.none()) {
             return null;
           }
         }
         Node r = getReferencedNode(referenceable);
         if (r == null) {
           return null;
         } else {
           return new IHyperlink[] {new CeylonNodeLink(r, id)};
         }
       }
     }
   }
 }
  public static String getInitialValueDescription(
      final Declaration dec, CeylonParseController cpc) {
    if (cpc != null) {
      Node refnode = getReferencedNode(dec);
      Tree.SpecifierOrInitializerExpression sie = null;
      String arrow = null;
      if (refnode instanceof Tree.AttributeDeclaration) {
        Tree.AttributeDeclaration ad = (Tree.AttributeDeclaration) refnode;
        sie = ad.getSpecifierOrInitializerExpression();
        arrow = " = ";
      } else if (refnode instanceof Tree.MethodDeclaration) {
        Tree.MethodDeclaration md = (Tree.MethodDeclaration) refnode;
        sie = md.getSpecifierExpression();
        arrow = " => ";
      }
      Tree.CompilationUnit lcu = cpc.getLastCompilationUnit();
      if (sie == null) {
        class FindInitializerVisitor extends Visitor {
          Tree.SpecifierOrInitializerExpression result;

          @Override
          public void visit(Tree.InitializerParameter that) {
            super.visit(that);
            Declaration d = that.getParameterModel().getModel();
            if (d != null && d.equals(dec)) {
              result = that.getSpecifierExpression();
            }
          }
        }
        FindInitializerVisitor fiv = new FindInitializerVisitor();
        fiv.visit(lcu);
        sie = fiv.result;
      }
      if (sie != null) {
        Tree.Expression e = sie.getExpression();
        if (e != null) {
          Tree.Term term = e.getTerm();
          if (term instanceof Tree.Literal) {
            String text = term.getToken().getText();
            if (text.length() < 20) {
              return arrow + text;
            }
          } else if (term instanceof Tree.BaseMemberOrTypeExpression) {
            Tree.BaseMemberOrTypeExpression bme = (Tree.BaseMemberOrTypeExpression) term;
            Tree.Identifier id = bme.getIdentifier();
            if (id != null && bme.getTypeArguments() == null) {
              return arrow + id.getText();
            }
          } else if (term != null) {
            Unit unit = lcu.getUnit();
            if (term.getUnit().equals(unit)) {
              String impl = Nodes.text(term, cpc.getTokens());
              if (impl.length() < 10) {
                return arrow + impl;
              }
            }
          }
          // don't have the token stream :-/
          // TODO: figure out where to get it from!
          return arrow + "...";
        }
      }
    }
    return "";
  }
  private boolean terminateWithBrace() throws Exception {
    IDocument doc = editor.getCeylonSourceViewer().getDocument();
    final TextChange change = new DocumentChange("Terminate Statement", doc);
    change.setEdit(new MultiTextEdit());
    CeylonParseController parser = parse();
    CompilationUnit rootNode = parser.getRootNode();
    IRegion li = getLineInfo(doc);
    final String lineText = doc.get(li.getOffset(), li.getLength());
    final List<CommonToken> tokens = parser.getTokens();
    final int startOfCodeInLine = getCodeStart(li, lineText, tokens);
    final int endOfCodeInLine = getCodeEnd(li, lineText, tokens);
    new Processor() {
      @Override
      public void visit(Tree.Expression that) {
        super.visit(that);
        if (that.getStopIndex() <= endOfCodeInLine && that.getStartIndex() >= startOfCodeInLine) {
          Token et = that.getMainEndToken();
          Token st = that.getMainToken();
          if (st != null
              && st.getType() == CeylonLexer.LPAREN
              && (et == null || et.getType() != CeylonLexer.RPAREN)) {
            if (!change.getEdit().hasChildren()) {
              change.addEdit(new InsertEdit(that.getStopIndex() + 1, ")"));
            }
          }
        }
      }

      @Override
      public void visit(Tree.ParameterList that) {
        super.visit(that);
        terminate(that, CeylonLexer.RPAREN, ")");
      }

      public void visit(Tree.IndexExpression that) {
        super.visit(that);
        terminate(that, CeylonLexer.RBRACKET, "]");
      }

      @Override
      public void visit(Tree.TypeParameterList that) {
        super.visit(that);
        terminate(that, CeylonLexer.LARGER_OP, ">");
      }

      @Override
      public void visit(Tree.TypeArgumentList that) {
        super.visit(that);
        terminate(that, CeylonLexer.LARGER_OP, ">");
      }

      @Override
      public void visit(Tree.PositionalArgumentList that) {
        super.visit(that);
        if (that.getToken().getType() == CeylonLexer.LPAREN) { // for infix function syntax
          terminate(that, CeylonLexer.RPAREN, ")");
        }
      }

      @Override
      public void visit(Tree.NamedArgumentList that) {
        super.visit(that);
        terminate(that, CeylonLexer.RBRACE, " }");
      }

      @Override
      public void visit(Tree.SequenceEnumeration that) {
        super.visit(that);
        terminate(that, CeylonLexer.RBRACE, " }");
      }

      @Override
      public void visit(Tree.IterableType that) {
        super.visit(that);
        terminate(that, CeylonLexer.RBRACE, "}");
      }

      @Override
      public void visit(Tree.Tuple that) {
        super.visit(that);
        terminate(that, CeylonLexer.RBRACKET, "]");
      }

      @Override
      public void visit(Tree.TupleType that) {
        super.visit(that);
        terminate(that, CeylonLexer.RBRACKET, "]");
      }

      @Override
      public void visit(Tree.ConditionList that) {
        super.visit(that);
        initiate(that, CeylonLexer.LPAREN, "(");
        // does not really work right:
        terminate(that, CeylonLexer.RPAREN, ")");
      }

      @Override
      public void visit(Tree.ForIterator that) {
        super.visit(that);
        initiate(that, CeylonLexer.LPAREN, "(");
        // does not really work right:
        terminate(that, CeylonLexer.RPAREN, ")");
      }

      @Override
      public void visit(Tree.ImportMemberOrTypeList that) {
        super.visit(that);
        terminate(that, CeylonLexer.RBRACE, " }");
      }

      @Override
      public void visit(Tree.ImportModule that) {
        super.visit(that);
        terminate(that, CeylonLexer.SEMICOLON, ";");
      }

      @Override
      public void visit(Tree.ImportModuleList that) {
        super.visit(that);
        terminate(that, CeylonLexer.RBRACE, " }");
      }

      @Override
      public void visit(Tree.PackageDescriptor that) {
        super.visit(that);
        terminate(that, CeylonLexer.SEMICOLON, ";");
      }

      @Override
      public void visit(Tree.Body that) {
        super.visit(that);
        terminate(that, CeylonLexer.RBRACE, " }");
      }

      @Override
      public void visit(Tree.StatementOrArgument that) {
        super.visit(that);
        if (
        /*that instanceof Tree.ExecutableStatement &&
        	!(that instanceof Tree.ControlStatement) ||
        that instanceof Tree.AttributeDeclaration ||
        that instanceof Tree.MethodDeclaration ||
        that instanceof Tree.ClassDeclaration ||
        that instanceof Tree.InterfaceDeclaration ||*/
        that instanceof Tree.SpecifiedArgument) {
          terminate(that, CeylonLexer.SEMICOLON, ";");
        }
      }

      private void initiate(Node that, int tokenType, String ch) {
        if (that.getStartIndex() >= startOfCodeInLine && that.getStartIndex() <= endOfCodeInLine) {
          Token et = that.getMainToken();
          if (et == null || et.getType() != tokenType || et.getText().startsWith("<missing ")) {
            if (!change.getEdit().hasChildren()) {
              change.addEdit(new InsertEdit(that.getStartIndex(), ch));
            }
          }
        }
      }

      private void terminate(Node that, int tokenType, String ch) {
        if (that.getStartIndex() >= startOfCodeInLine && that.getStartIndex() <= endOfCodeInLine) {
          Token et = that.getMainEndToken();
          if ((et == null || et.getType() != tokenType) || that.getStopIndex() > endOfCodeInLine) {
            if (!change.getEdit().hasChildren()) {
              change.addEdit(new InsertEdit(min(endOfCodeInLine, that.getStopIndex()) + 1, ch));
            }
          }
        }
      }
    }.visit(rootNode);
    if (change.getEdit().hasChildren()) {
      change.perform(new NullProgressMonitor());
      return true;
    }
    return false;
  }