Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,24 @@ boolean collectTasks() {
}

// current PHP formatter must run after HTML formatter
// For PHP files, the CSS formatter should run after the HTML formatter and JS formatter, but before the PHP formatter.
if (items != null && "text/x-php5".equals(docMimeType())) { //NOI18N
// Copy list, except for Ruby element, which we then add at the end
List<MimeItem> newItems = new ArrayList<MimeItem>(items.size());
MimeItem phpItem = null;
MimeItem cssItem = null;
for (MimeItem item : items) {
if (item.mimePath().getPath().endsWith("text/x-php5")) { // NOI18N
phpItem = item;
} else if (item.mimePath().getPath().endsWith("text/css")) {
cssItem = item;
} else {
newItems.add(item);
}
}
if (cssItem != null) {
newItems.add(cssItem);
}
if (phpItem != null) {
newItems.add(phpItem);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.netbeans.modules.php.editor.embedding;

import java.awt.Color;
import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import org.netbeans.api.editor.mimelookup.MimeLookup;
import org.netbeans.api.editor.settings.AttributesUtilities;
import org.netbeans.api.editor.settings.FontColorSettings;
import org.netbeans.api.lexer.Language;
import org.netbeans.api.lexer.LanguagePath;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.api.lexer.TokenHierarchyEvent;
import org.netbeans.api.lexer.TokenHierarchyListener;
import org.netbeans.api.lexer.TokenSequence;
import static org.netbeans.modules.php.api.util.FileUtils.PHP_MIME_TYPE;
import org.netbeans.spi.editor.highlighting.HighlightsSequence;
import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer;
import org.openide.util.Lookup.Result;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.WeakListeners;

/**
* The class {@link org.netbeans.modules.html.editor.coloring.EmbeddingHighlightsContainer} is taken as a basis
*/
public class CssEmbeddingHighlightsContainer extends AbstractHighlightsContainer implements TokenHierarchyListener {

private static final Logger LOG = Logger.getLogger(CssEmbeddingHighlightsContainer.class.getName());

private static final String CSS_BACKGROUND_TOKEN_NAME = "css-embedding"; //NOI18N
private static final String CSS_MIME_TYPE = "text/css"; //NOI18N

private final Document document;
private AttributeSet cssBackground;
private TokenHierarchy<? extends Document> hierarchy = null;
private long version = 0;

private Result<FontColorSettings> lookupResult;
private LookupListener lookupListener;

CssEmbeddingHighlightsContainer(Document document) {
this.document = document;

lookupResult = MimeLookup.getLookup(PHP_MIME_TYPE).lookupResult(FontColorSettings.class);
lookupResult.addLookupListener(WeakListeners.create(LookupListener.class,
lookupListener = new LookupListener() {
@Override
public void resultChanged(LookupEvent ev) {
refreshColorings();
}
}, lookupResult));
refreshColorings();
}

private void refreshColorings() {
Collection<? extends FontColorSettings> allInstances = lookupResult.allInstances();
assert allInstances.size() > 0;

FontColorSettings fcs = allInstances.iterator().next();
Color cssBC = null;
if (fcs != null) {
cssBC = getColoring(fcs, CSS_BACKGROUND_TOKEN_NAME);
}

cssBackground = cssBC == null ? SimpleAttributeSet.EMPTY : AttributesUtilities.createImmutable(
StyleConstants.Background, cssBC,
ATTR_EXTENDS_EOL, Boolean.TRUE);
}

@Override
public HighlightsSequence getHighlights(int startOffset, int endOffset) {
synchronized (this) {
if (cssBackground != null) {
if (hierarchy == null) {
hierarchy = TokenHierarchy.get(document);
if (hierarchy != null) {
hierarchy.addTokenHierarchyListener(WeakListeners.create(TokenHierarchyListener.class, this, hierarchy));
}
}

if (hierarchy != null) {
return new Highlights(version, hierarchy, startOffset, endOffset);
}
}
return HighlightsSequence.EMPTY;
}
}

// ----------------------------------------------------------------------
// TokenHierarchyListener implementation
// ----------------------------------------------------------------------

@Override
public void tokenHierarchyChanged(TokenHierarchyEvent evt) {
synchronized (this) {
version++;
}

fireHighlightsChange(evt.affectedStartOffset(), evt.affectedEndOffset());
}

// ----------------------------------------------------------------------
// Private implementation
// ----------------------------------------------------------------------

private static Color getColoring(FontColorSettings fcs, String tokenName) {
AttributeSet as = fcs.getTokenFontColors(tokenName);
if (as != null) {
return (Color) as.getAttribute(StyleConstants.Background); //NOI18N
}
return null;
}

private class Highlights implements HighlightsSequence {

private final long version;
private final TokenHierarchy<? extends Document> scanner;
private final int startOffsetBoundary;
private final int endOffsetBoundary;

private List<TokenSequence<?>> tokenSequenceList = null;
private int startOffset;
private int endOffset;
private int realEndOffset;
private AttributeSet attributeSet;
private boolean finished = false;

private Highlights(long version, TokenHierarchy<? extends Document> scanner, int startOffset, int endOffset) {
this.version = version;
this.scanner = scanner;
this.startOffsetBoundary = startOffset;
this.endOffsetBoundary = endOffset;
}

private boolean _moveNext() {
if (tokenSequenceList == null) {
// initialize
this.startOffset = startOffsetBoundary;
this.endOffset = startOffsetBoundary;
this.realEndOffset = startOffsetBoundary;

String mimeType = (String) document.getProperty ("mimeType"); //NOI18N
Language<?> language = Language.find(mimeType);
if (language != null) {
//get php token sequence list
LanguagePath topLevelLanguagePath = LanguagePath.get(language);
if (mimeType.equals(PHP_MIME_TYPE)) {
tokenSequenceList = scanner.tokenSequenceList(topLevelLanguagePath, startOffsetBoundary, endOffsetBoundary);
}
} else {
LOG.log(Level.WARNING, "Language " + mimeType + " obtained from the document mimeType property cannot be found!"); //NOI18N
}
}

if (tokenSequenceList != null) {
for (TokenSequence tokenSequence : tokenSequenceList) {
assert tokenSequence.language().mimeType().equals(PHP_MIME_TYPE);
tokenSequence.move(realEndOffset);
while (tokenSequence.moveNext() && tokenSequence.offset() < endOffsetBoundary) {
TokenSequence eTokenSequence = tokenSequence.embedded();

if (eTokenSequence == null || !eTokenSequence.moveNext()) {
continue;
}

String embeddedMimeType = eTokenSequence.language().mimeType();
if ((CSS_MIME_TYPE).equals(embeddedMimeType)) {
eTokenSequence.move(realEndOffset);
if(eTokenSequence.moveNext()) {
startOffset = eTokenSequence.offset();
do {
endOffset = eTokenSequence.offset() + eTokenSequence.token().length();
} while (eTokenSequence.moveNext());
realEndOffset = endOffset > realEndOffset ? endOffset : realEndOffset + 1;

attributeSet = cssBackground;
if (attributeSet != null) {
return true;
}
}
}
}
attributeSet = cssBackground;
if (attributeSet != null) {
return true;
}
}
}

return false;
}

@Override
public boolean moveNext() {
synchronized (CssEmbeddingHighlightsContainer.this) {
if (checkVersion()) {
if (_moveNext()) {
return true;
}
}
}

finished = true;
return false;
}

@Override
public int getStartOffset() {
synchronized (CssEmbeddingHighlightsContainer.this) {
if (finished) {
throw new NoSuchElementException();
} else {
assert tokenSequenceList != null : "Sequence not initialized, call moveNext() first."; //NOI18N
return startOffset;
}
}
}

@Override
public int getEndOffset() {
synchronized (CssEmbeddingHighlightsContainer.this) {
if (finished) {
throw new NoSuchElementException();
} else {
assert tokenSequenceList != null : "Sequence not initialized, call moveNext() first."; //NOI18N
return endOffset;
}
}
}

@Override
public AttributeSet getAttributes() {
synchronized (CssEmbeddingHighlightsContainer.this) {
if (finished) {
throw new NoSuchElementException();
} else {
assert tokenSequenceList != null : "Sequence not initialized, call moveNext() first."; //NOI18N
return attributeSet;
}
}
}

private boolean checkVersion() {
return this.version == CssEmbeddingHighlightsContainer.this.version;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.php.editor.embedding;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.api.lexer.TokenId;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.modules.parsing.api.Embedding;
import org.netbeans.modules.parsing.api.Snapshot;
import org.netbeans.modules.parsing.spi.EmbeddingProvider;
import static org.netbeans.modules.php.api.util.FileUtils.PHP_MIME_TYPE;
import static org.netbeans.modules.php.editor.embedding.CssEmbeddingProvider.TARGET_MIME_TYPE;
import static org.netbeans.modules.php.editor.lexer.PHPTokenId.T_EMBEDDED_CSS;

/**
* This class is a copy of the {@link org.netbeans.modules.javascript2.vue.editor.embedding.VueCssEmbeddingProvider}
* with minor changes
*/
@EmbeddingProvider.Registration(
mimeType = PHP_MIME_TYPE,
targetMimeType = TARGET_MIME_TYPE)
public class CssEmbeddingProvider extends EmbeddingProvider {

public static final String TARGET_MIME_TYPE = "text/css"; //NOI18N

private volatile boolean cancelled = true;

@Override
public List<Embedding> getEmbeddings(Snapshot snapshot) {
cancelled = false;
TokenHierarchy<?> tokenHierarchy = snapshot.getTokenHierarchy();
TokenSequence<?> ts = tokenHierarchy.tokenSequence();

if (ts == null || !ts.isValid()) {
return Collections.emptyList();
}

ts.moveStart();

List<Embedding> embeddings = new ArrayList<>();

while (ts.moveNext()) {
if (cancelled) {
embeddings.clear();
break;
}
Token<?> token = ts.token();
TokenId id = token.id();
if (id.equals(T_EMBEDDED_CSS)) {
embeddings.add(snapshot.create(ts.offset(), token.length(), TARGET_MIME_TYPE));
}
}

if (embeddings.isEmpty()) {
return Collections.emptyList();
}
return Collections.singletonList(Embedding.create(embeddings));
}

@Override
public int getPriority() {
return 200;
}

@Override
public void cancel() {
cancelled = true;
}
}
Loading
Loading