Package ccw.repl

Source Code of ccw.repl.NamespaceBrowser

package ccw.repl;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;

import ccw.CCWPlugin;
import ccw.ClojureCore;
import ccw.TraceOptions;
import ccw.util.ClojureDocUtils;
import ccw.util.ClojureInvoker;
import ccw.util.DisplayUtil;
import clojure.lang.Keyword;
import clojure.tools.nrepl.Connection.Response;

public class NamespaceBrowser extends ViewPart implements ISelectionProvider, ISelectionChangedListener {
  /**
   * The plugin prefix.
   */
  public static final String PREFIX = CCWPlugin.PLUGIN_ID + "."; //$NON-NLS-1$

  /**
   * Help context id used for the content outline view (value
   * <code>"org.eclipse.ui.content_outline_context"</code>).
   */
  public static final String CONTENT_OUTLINE_VIEW_HELP_CONTEXT_ID = PREFIX + "content_outline_context";//$NON-NLS-1$

  private static final String NS_BROWSER_REFRESH_FAMILY = "ccw.outline.job.refresh";
  private static final Keyword KEYWORD_NAME = Keyword.intern(null, "name");
  private static final Keyword KEYWORD_CHILDREN = Keyword.intern(null, "children");
  private static final Keyword KEYWORD_TYPE = Keyword.intern(null, "type");
  public static final Keyword KEYWORD_PRIVATE = Keyword.intern(null, "private");
  public static final Keyword KEYWORD_NS = Keyword.intern(null, "ns");
  private static final Keyword KEYWORD_FILE = Keyword.intern(null, "file");
  private static final Keyword KEYWORD_LINE = Keyword.intern(null, "line");

  private ListenerList selectionChangedListeners = new ListenerList();

  private TreeViewer treeViewer;

  private Composite control;
  private Text filterText;
  private String patternString = "";
  private Pattern pattern;
  private ISelection selectionBeforePatternSearchBegan;
  private Object[] expandedElementsBeforeSearchBegan;

  private final ClojureInvoker docUtils =
      ClojureInvoker.newInvoker(
          CCWPlugin.getDefault(),
          "ccw.core.doc-utils");

  private static final String VAR_DOC_INFO = "var-doc-info-text";

  /**
   * Creates a content outline view with no content outline pages.
   */
  public NamespaceBrowser() {
    super();
  }

  @Override
  public void init(IViewSite site) throws PartInitException {
    super.init(site);
    site.setSelectionProvider(this);
  }

  /**
   * The <code>PageBookView</code> implementation of this
   * <code>IWorkbenchPart</code> method creates a <code>PageBook</code>
   * control with its default page showing.
   */
  @Override
  public void createPartControl(Composite theParent) {
    control = new Composite(theParent, SWT.NONE);

    GridLayout gl = new GridLayout();
    gl.numColumns = 2;
    control.setLayout(gl);

    Label l = new Label(control, SWT.NONE);
    l.setText("Find :");
    l.setToolTipText("Enter an expression on which the browser will filter, based on name and doc string of vars");
    GridData gd = new GridData();
    gd.verticalAlignment = SWT.CENTER;
    l.setLayoutData(gd);

    filterText = new Text(control, SWT.FILL | SWT.BORDER);
    filterText.setToolTipText("Enter here a word to search. It can be a regexp. e.g. \"-map$\" (without double quotes) for matching strings ending with -map");
    gd = new GridData();
    gd.horizontalAlignment = SWT.FILL;
    gd.verticalAlignment = SWT.CENTER;
    gd.grabExcessHorizontalSpace = true;
    filterText.setLayoutData(gd);
    filterText.addModifyListener(new ModifyListener() {
      @Override
      public void modifyText(ModifyEvent e) {
        patternString = ((Text) e.getSource()).getText();
        if ("".equals(patternString.trim())) {
          if (pattern != null) {
            // user stops search, we restore the previous state of
            // the tree
            pattern = null;
            delayedRefresh(false);
            treeViewer.setExpandedElements(expandedElementsBeforeSearchBegan);
            treeViewer.setSelection(selectionBeforePatternSearchBegan);
            selectionBeforePatternSearchBegan = null;
            expandedElementsBeforeSearchBegan = null;
          }
        } else {
          pattern = Pattern.compile(patternString.trim());
          if (selectionBeforePatternSearchBegan == null) {
            // user triggers search, we save the current state of
            // the tree
            selectionBeforePatternSearchBegan = treeViewer.getSelection();
            expandedElementsBeforeSearchBegan = treeViewer.getExpandedElements();
          }
          delayedRefresh(false);
          treeViewer.expandAll();
        }
      }
    });

    treeViewer = new TreeViewer(control, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
    treeViewer.addSelectionChangedListener(this);
    gd = new GridData();// SWT.FILL, SWT.FILL, true, true, 2, 1);
    gd.horizontalSpan = 2;
    gd.horizontalAlignment = SWT.FILL;
    gd.grabExcessHorizontalSpace = true;
    gd.verticalAlignment = SWT.FILL;
    gd.grabExcessVerticalSpace = true;
    treeViewer.getControl().setLayoutData(gd);

    ColumnViewerToolTipSupport.enableFor(treeViewer);

    treeViewer.setContentProvider(new ContentProvider());
    treeViewer.setLabelProvider(new LabelProvider());

    treeViewer.addFilter(new ViewerFilter() {
      @Override
      public boolean select(Viewer viewer, Object parentElement, Object element) {
        if (patternString == null || patternString.trim().equals("")) {
          return true;
        } else {
          return recursiveElemMatches(element);
        }
      }

      /** Tests element node, and its children if necessary, recursively */
      private boolean recursiveElemMatches(Object element) {
        if (elemMatches(element)) {
          return true;
        } else {
          ITreeContentProvider cp = (ITreeContentProvider) treeViewer.getContentProvider();
          if (cp.hasChildren(element)) {
            for (Object c: cp.getChildren(element)) {
              if (recursiveElemMatches(c)) {
                return true;
              }
            }
            return false;
          } else {
            return false;
          }
        }
      }

      /** Test just element node, not its children */
      private boolean elemMatches(Object element) {
        Map elem = (Map) element;
        String name = (String) elem.get(KEYWORD_NAME);
        boolean nameMatches = name != null && pattern.matcher(name).find();

        String doc = (String) elem.get(ClojureDocUtils.KEYWORD_DOC);
        boolean docMatches = doc != null && pattern.matcher(doc).find();

        return nameMatches || docMatches;
      }
    });

    treeViewer.addDoubleClickListener(new IDoubleClickListener() {
      @Override
      public void doubleClick(DoubleClickEvent event) {
        IStructuredSelection sel = (IStructuredSelection) event.getSelection();
        if (sel.size() == 0)
          return;

        Map node = (Map) sel.getFirstElement();

        if (!"var".equals(node.get(KEYWORD_TYPE)))
          return;

        String searchedNS = ((String) node.get(KEYWORD_NS));
        String searchedFileName = (String) node.get(KEYWORD_FILE);
        int line = (node.get(KEYWORD_LINE) == null) ? -1 : Integer.valueOf((String) node.get(KEYWORD_LINE));
        ClojureCore.openInEditor(searchedNS, searchedFileName, line);
      }
    });
    // PlatformUI.getWorkbench().getHelpSystem().setHelp(getPageBook(),
    // CONTENT_OUTLINE_VIEW_HELP_CONTEXT_ID);
  }

  @Override
  public Object getAdapter(Class key) {
    return super.getAdapter(key);
  }

  private static class ContentProvider implements ITreeContentProvider {
    @Override
    public Object[] getElements(Object inputElement) {
      return getChildren(inputElement);
    }

    @Override
    public void dispose() {
    }

    @Override
    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
    }

    @Override
    public Object[] getChildren(Object parentElement) {
      if (Map.class.isInstance(parentElement)) {
        Collection children = (Collection) ((Map) parentElement).get(KEYWORD_CHILDREN);
        if (children == null) {
          return new Object[0];
        } else {
          return children.toArray();
        }
      } else {
        return new Object[0];
      }
    }

    @Override
    public Object getParent(Object element) {
      return null;
    }

    @Override
    public boolean hasChildren(Object parentElement) {
      if (Map.class.isInstance(parentElement)) {
        return ((Map) parentElement).get(KEYWORD_CHILDREN) != null;
      } else {
        return false;
      }
    }
  }

  private class LabelProvider extends CellLabelProvider {

    @Override
    public String getToolTipText(Object element) {
      return (String) docUtils._(VAR_DOC_INFO, element);
    }

    @Override
    public Point getToolTipShift(Object object) {
      return new Point(5, 15);
    }

    @Override
    public int getToolTipDisplayDelayTime(Object object) {
      return 100;
    }

    @Override
    public int getToolTipTimeDisplayed(Object object) {
      return 15000;
    }

    @Override
    public void update(ViewerCell cell) {
      cell.setText(getText(cell.getElement()));
      cell.setImage(getImage(cell.getElement()));

    }

    private String getText(Object element) {
      if (Map.class.isInstance(element)) {
        return (String) ((Map) element).get(KEYWORD_NAME);
      } else {
        return element.toString();
      }
    }

    private Image getImage(Object element) {
      if (Map.class.isInstance(element)) {
        Map node = (Map) element;
        if ("ns".equals(node.get(KEYWORD_TYPE))) {
          return CCWPlugin.getDefault().getImageRegistry().get(CCWPlugin.NS);
        } else {
          if ("true".equals(node.get(KEYWORD_PRIVATE))) {
            return CCWPlugin.getDefault().getImageRegistry().get(CCWPlugin.PRIVATE_FUNCTION);
          } else {
            return CCWPlugin.getDefault().getImageRegistry().get(CCWPlugin.PUBLIC_FUNCTION);
          }
        }
      }
      return null;
    }
  }

  @SuppressWarnings("unchecked")
    private Map<String, List<String>> getRemoteNsTree (SafeConnection repl) throws Exception {
      Response res = repl.send(10000, "op", "eval", "code", "(ccw.debug.serverrepl/namespaces-info)");
        List<Object> values = res.values();
        if (values.isEmpty()) {
          return null;
        } else {
          return (Map<String, List<String>>)values.get(0);
        }
  }

  public void reset (final SafeConnection repl) {
    if (repl == null) {
      asyncResetInput(null);
      return;
    }
    Job job = new Job("Namespace browser tree refresh") {
      @Override
      protected IStatus run(IProgressMonitor monitor) {
        if (treeViewer == null) {
          return Status.CANCEL_STATUS;
        }
        try {
          Object oldInput = treeViewer.getInput();
          final Object newInput = getRemoteNsTree(repl);
          if (oldInput != null && oldInput.equals(newInput)) {
            return Status.CANCEL_STATUS;
          } else {
            asyncResetInput(newInput);
            return Status.OK_STATUS;
          }
        } catch (Exception e) {
          e.printStackTrace();
          repl.connectionLost();
          asyncResetInput(null);
          return Status.OK_STATUS;
        }
      }
      @Override
      public boolean belongsTo(Object family) {
        return NS_BROWSER_REFRESH_FAMILY.equals(family);
      }
    };
    job.setSystem(true);
    Job.getJobManager().cancel(NS_BROWSER_REFRESH_FAMILY);
    job.schedule(200);
  }

  public void delayedRefresh(final boolean updateLabels) {
    Job job = new Job("Namespace browser tree refresh") {
      @Override
      protected IStatus run(IProgressMonitor monitor) {
        if (treeViewer == null) {
          return Status.CANCEL_STATUS;
        }

        if (Display.getCurrent() == null) {
          final Display display = PlatformUI.getWorkbench().getDisplay();
          display.asyncExec(new Runnable() {
            @Override
            public void run() {
//              System.out.println("delayed refresh start");
              treeViewer.refresh(updateLabels);
//              System.out.println("delayed refresh stop");
            }
          });
        } else {
          treeViewer.refresh(updateLabels);
        }
        return Status.OK_STATUS;
      }
      @Override
      public boolean belongsTo(Object family) {
        return NS_BROWSER_REFRESH_FAMILY.equals(family);
      }
    };
    job.setSystem(true);
    Job.getJobManager().cancel(NS_BROWSER_REFRESH_FAMILY);
    job.schedule(500);
  }

  private void asyncResetInput(final Object newInput) {
    DisplayUtil.asyncExec(new Runnable() {
      @Override public void run() {
        ISelection sel = treeViewer.getSelection();
        TreePath[] expandedTreePaths = treeViewer.getExpandedTreePaths();

        treeViewer.setInput(newInput);
        if (newInput != null) {
          treeViewer.setExpandedTreePaths(expandedTreePaths);
          treeViewer.setSelection(sel);
        }
      }
    });
  }

  @Override
  public void addSelectionChangedListener(ISelectionChangedListener listener) {
    selectionChangedListeners.add(listener);
  }

  /**
   * Fires a selection changed event.
   *
   * @param selection
   *            the new selection
   */
  protected void fireSelectionChanged(ISelection selection) {
    // create an event
    final SelectionChangedEvent event = new SelectionChangedEvent(this, selection);

    // fire the event
    Object[] listeners = selectionChangedListeners.getListeners();
    for (int i = 0; i < listeners.length; ++i) {
      final ISelectionChangedListener l = (ISelectionChangedListener) listeners[i];
      SafeRunner.run(new SafeRunnable() {
        @Override
        public void run() {
          l.selectionChanged(event);
        }
      });
    }
  }

  public Control getControl() {
    if (control == null) {
      return null;
    }
    return control;
  }

  @Override
  public ISelection getSelection() {
    if (treeViewer == null) {
      return StructuredSelection.EMPTY;
    }
    return treeViewer.getSelection();
  }

  @Override
  public void removeSelectionChangedListener(ISelectionChangedListener listener) {
    selectionChangedListeners.remove(listener);
  }

  @Override
  public void selectionChanged(SelectionChangedEvent event) {
    fireSelectionChanged(event.getSelection());
  }

  /**
   * Sets focus to a part in the page.
   */
  @Override
  public void setFocus() {
    if (treeViewer != null) {
      treeViewer.getControl().setFocus();
    }
  }

  @Override
  public void setSelection(ISelection selection) {
    if (treeViewer != null) {
      treeViewer.setSelection(selection);
    }
  }

  public static void setREPLConnection (final SafeConnection repl) {
        DisplayUtil.asyncExec(new Runnable() {
            @Override
      public void run() {
                inUIThreadSetREPLConnection(repl);
            }
        });
  }

  private static void inUIThreadSetREPLConnection (SafeConnection repl) {
    IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
    if (activeWorkbenchWindow == null ) {
      CCWPlugin.getTracer().trace(TraceOptions.REPL, "activeWorkbenchWindow is null");
      return;
    }
    IWorkbenchPage activePage = activeWorkbenchWindow.getActivePage();
    if (activePage == null) {
      CCWPlugin.getTracer().trace(TraceOptions.REPL, "activePage is null");
    }
    IViewPart[] views = activePage.getViews();
    NamespaceBrowser co = null;
    for (IViewPart v: views) {
      if (NamespaceBrowser.class.isInstance(v)) {
        co = (NamespaceBrowser) v;
        break;
      }
    }
    if (co == null) {
      return;
    }

    co.reset(repl);
  }

}
TOP

Related Classes of ccw.repl.NamespaceBrowser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.