Skip to content

Commit

Permalink
Initial changes to support semantic highlighting
Browse files Browse the repository at this point in the history
Closes eclipse-jdtls#715.

Signed-off-by: Akos Kitta <kittaakos@typefox.io>
  • Loading branch information
Akos Kitta committed Jul 4, 2018
1 parent b695ed2 commit a9ebadd
Show file tree
Hide file tree
Showing 10 changed files with 2,470 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
*******************************************************************************/
package org.eclipse.jdt.ls.core.internal.handlers;

import static com.google.common.collect.Lists.newArrayList;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
Expand Down Expand Up @@ -50,7 +52,10 @@
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
import org.eclipse.jdt.ls.core.internal.preferences.Preferences;
import org.eclipse.jdt.ls.core.internal.preferences.Preferences.Severity;
import org.eclipse.jdt.ls.core.internal.semantichighlight.SemanticHighlightingService;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
Expand All @@ -75,12 +80,15 @@ public class DocumentLifeCycleHandler {
private CoreASTProvider sharedASTProvider;
private WorkspaceJob validationTimer;
private Set<ICompilationUnit> toReconcile = new HashSet<>();
private SemanticHighlightingService semanticHighlightingService;

public DocumentLifeCycleHandler(JavaClientConnection connection, PreferenceManager preferenceManager, ProjectsManager projectsManager, boolean delayValidation) {
this.connection = connection;
this.preferenceManager = preferenceManager;
this.projectsManager = projectsManager;
this.sharedASTProvider = CoreASTProvider.getInstance();
boolean semanticHighlightingSupported = this.preferenceManager.getClientPreferences().isSemanticHighlightingSupported();
this.semanticHighlightingService = new SemanticHighlightingService(this.connection, semanticHighlightingSupported);
if (delayValidation) {
this.validationTimer = new WorkspaceJob("Validate documents") {
@Override
Expand Down Expand Up @@ -281,6 +289,7 @@ public void handleOpen(DidOpenTextDocumentParams params) {
buffer.setContents(newContent);
}
triggerValidation(unit);
installSemanticHighlightings(uri);
// see https://github.com/redhat-developer/vscode-java/issues/274
checkPackageDeclaration(uri, unit);
} catch (JavaModelException e) {
Expand All @@ -289,7 +298,8 @@ public void handleOpen(DidOpenTextDocumentParams params) {
}

public void handleChanged(DidChangeTextDocumentParams params) {
ICompilationUnit unit = JDTUtils.resolveCompilationUnit(params.getTextDocument().getUri());
String uri = params.getTextDocument().getUri();
ICompilationUnit unit = JDTUtils.resolveCompilationUnit(uri);

if (unit == null || !unit.isWorkingCopy() || params.getContentChanges().isEmpty() || unit.getResource().isDerived()) {
return;
Expand All @@ -300,6 +310,7 @@ public void handleChanged(DidChangeTextDocumentParams params) {
sharedASTProvider.disposeAST();
}
List<TextDocumentContentChangeEvent> contentChanges = params.getContentChanges();
List<DocumentEvent> contentChangeEvents = newArrayList();
for (TextDocumentContentChangeEvent changeEvent : contentChanges) {

Range range = changeEvent.getRange();
Expand All @@ -326,9 +337,11 @@ public void handleChanged(DidChangeTextDocumentParams params) {
}
IDocument document = JsonRpcHelpers.toDocument(unit.getBuffer());
edit.apply(document, TextEdit.NONE);
contentChangeEvents.add(new DocumentEvent(document, startOffset, length, text));
}
triggerValidation(unit);
} catch (JavaModelException | MalformedTreeException | BadLocationException e) {
updateSemanticHighlightings(uri, contentChangeEvents);
} catch (JavaModelException | MalformedTreeException | BadLocationException | BadPositionCategoryException e) {
JavaLanguageServerPlugin.logException("Error while handling document change", e);
}
}
Expand All @@ -347,6 +360,7 @@ public void handleClosed(DidCloseTextDocumentParams params) {
sharedASTProvider.disposeAST();
}
unit.discardWorkingCopy();
uninstallSemanticHighlightings(uri);
} catch (CoreException e) {
JavaLanguageServerPlugin.logException("Error while handling document close", e);
}
Expand Down Expand Up @@ -416,4 +430,16 @@ private ICompilationUnit checkPackageDeclaration(String uri, ICompilationUnit un
return unit;
}

private void installSemanticHighlightings(String uri) {
this.semanticHighlightingService.install(uri);
}

private void uninstallSemanticHighlightings(String uri) {
this.semanticHighlightingService.uninstall(uri);
}

private void updateSemanticHighlightings(String uri, List<DocumentEvent> contentChangeEvents) throws BadLocationException, BadPositionCategoryException {
this.semanticHighlightingService.update(uri, contentChangeEvents);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,9 @@ public boolean isSupportsCompletionDocumentationMarkdown() {
public boolean isWorkspaceEditResourceChangesSupported() {
return capabilities.getWorkspace() != null && capabilities.getWorkspace().getWorkspaceEdit() != null && isTrue(capabilities.getWorkspace().getWorkspaceEdit().getResourceChanges());
}

public boolean isSemanticHighlightingSupported() {
// TODO should be ClientCapabilities -> TextDocument -> SemanticHighlighting once there is a LSP4J o2 site.
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright (c) 2018 TypeFox and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Copied from https://github.com/eclipse/eclipse.jdt.ui/blob/d41fa3326c5b75a6419c81fcecb37d7d7fb3ac43/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javaeditor/SemanticHighlighting.java
*******************************************************************************/

package org.eclipse.jdt.ls.core.internal.semantichighlight;

import java.util.Collection;

import com.google.common.collect.Iterables;

/**
* Semantic highlighting
*/
public interface SemanticHighlighting {

public abstract Collection<String> getScopes();

/**
* @return the display name
*/
public default String getDisplayName() {
final StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getSimpleName());
sb.append("[");
sb.append(Iterables.toString(getScopes()));
sb.append("]");
return sb.toString();
}

/**
* Returns <code>true</code> iff the semantic highlighting consumes the semantic
* token.
* <p>
* NOTE: Implementors are not allowed to keep a reference on the token or on any
* object retrieved from the token.
* </p>
*
* @param token
* the semantic token for a
* {@link org.eclipse.jdt.core.dom.SimpleName}
* @return <code>true</code> iff the semantic highlighting consumes the semantic
* token
*/
public boolean consumes(SemanticToken token);

/**
* Returns <code>true</code> iff the semantic highlighting consumes the semantic
* token.
* <p>
* NOTE: Implementors are not allowed to keep a reference on the token or on any
* object retrieved from the token.
* </p>
*
* @param token
* the semantic token for a
* {@link org.eclipse.jdt.core.dom.NumberLiteral},
* {@link org.eclipse.jdt.core.dom.BooleanLiteral} or
* {@link org.eclipse.jdt.core.dom.CharacterLiteral}
* @return <code>true</code> iff the semantic highlighting consumes the semantic
* token
*/
public default boolean consumesLiteral(SemanticToken token) {
return false;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*******************************************************************************
* Copyright (c) 2018 TypeFox and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Copied from https://github.com/eclipse/eclipse.jdt.ui/blob/d41fa3326c5b75a6419c81fcecb37d7d7fb3ac43/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javaeditor/SemanticHighlightingManager.java
*******************************************************************************/
package org.eclipse.jdt.ls.core.internal.semantichighlight;

import java.util.Collection;

import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;

/**
* Semantic highlighting manager
*
* @since 3.0
*/
public class SemanticHighlightingManager {

/**
* Highlighted Positions.
*/
static class HighlightedPosition extends Position {

/** Highlighting (TM) scopes of the position */
private Collection<String> fStyle;

/** Lock object */
private Object fLock;

/**
* Initialize the styled positions with the given offset, length and foreground
* color.
*
* @param offset
* The position offset
* @param length
* The position length
* @param highlighting
* The position's TM scopes for the highlighting
* @param lock
* The lock object
*/
public HighlightedPosition(int offset, int length, Collection<String> highlighting, Object lock) {
super(offset, length);
fStyle= highlighting;
fLock= lock;
}

/**
* Uses reference equality for the highlighting.
*
* @param off
* The offset
* @param len
* The length
* @param highlighting
* The highlighting scopes for the position
* @return <code>true</code> iff the given offset, length and highlighting are
* equal to the internal ones.
*/
public boolean isEqual(int off, int len, Collection<String> highlighting) {
synchronized (fLock) {
return !isDeleted() && getOffset() == off && getLength() == len && fStyle == highlighting;
}
}

/**
* Is this position contained in the given range (inclusive)? Synchronizes on position updater.
*
* @param off The range offset
* @param len The range length
* @return <code>true</code> iff this position is not delete and contained in the given range.
*/
public boolean isContained(int off, int len) {
synchronized (fLock) {
return !isDeleted() && off <= getOffset() && off + len >= getOffset() + getLength();
}
}

public void update(int off, int len) {
synchronized (fLock) {
super.setOffset(off);
super.setLength(len);
}
}

/*
* @see org.eclipse.jface.text.Position#setLength(int)
*/
@Override
public void setLength(int length) {
synchronized (fLock) {
super.setLength(length);
}
}

/*
* @see org.eclipse.jface.text.Position#setOffset(int)
*/
@Override
public void setOffset(int offset) {
synchronized (fLock) {
super.setOffset(offset);
}
}

/*
* @see org.eclipse.jface.text.Position#delete()
*/
@Override
public void delete() {
synchronized (fLock) {
super.delete();
}
}

/*
* @see org.eclipse.jface.text.Position#undelete()
*/
@Override
public void undelete() {
synchronized (fLock) {
super.undelete();
}
}

/**
* @return Returns the highlighting (TM) scopes.
*/
public Collection<String> getHighlightingScopes() {
return fStyle;
}
}

/**
* Highlighted ranges.
*/
public static class HighlightedRange extends Region {
/** The highlighting key as returned by {@link SemanticHighlighting#getPreferenceKey()}. */
private String fKey;

/**
* Initialize with the given offset, length and highlighting key.
*
* @param offset the offset
* @param length the length
* @param key the highlighting key as returned by
* {@link SemanticHighlighting#getPreferenceKey()}
*/
public HighlightedRange(int offset, int length, String key) {
super(offset, length);
fKey= key;
}

/**
* @return the highlighting key as returned by {@link SemanticHighlighting#getPreferenceKey()}
*/
public String getKey() {
return fKey;
}

/*
* @see org.eclipse.jface.text.Region#equals(java.lang.Object)
*/
@Override
public boolean equals(Object o) {
return super.equals(o) && o instanceof HighlightedRange && fKey.equals(((HighlightedRange)o).getKey());
}

/*
* @see org.eclipse.jface.text.Region#hashCode()
*/
@Override
public int hashCode() {
return super.hashCode() | fKey.hashCode();
}
}

}
Loading

0 comments on commit a9ebadd

Please sign in to comment.