Skip to content

Commit 568b554

Browse files
committed
PHP: Added CSS embedding to heredoc/nowdoc
1 parent 8b022c7 commit 568b554

File tree

13 files changed

+542
-0
lines changed

13 files changed

+542
-0
lines changed

ide/editor.indent/src/org/netbeans/modules/editor/indent/TaskHandler.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,17 +168,24 @@ boolean collectTasks() {
168168
}
169169

170170
// current PHP formatter must run after HTML formatter
171+
// For PHP files, the CSS formatter should run after the HTML formatter and JS formatter, but before the PHP formatter.
171172
if (items != null && "text/x-php5".equals(docMimeType())) { //NOI18N
172173
// Copy list, except for Ruby element, which we then add at the end
173174
List<MimeItem> newItems = new ArrayList<MimeItem>(items.size());
174175
MimeItem phpItem = null;
176+
MimeItem cssItem = null;
175177
for (MimeItem item : items) {
176178
if (item.mimePath().getPath().endsWith("text/x-php5")) { // NOI18N
177179
phpItem = item;
180+
} else if (item.mimePath().getPath().endsWith("text/css")) {
181+
cssItem = item;
178182
} else {
179183
newItems.add(item);
180184
}
181185
}
186+
if (cssItem != null) {
187+
newItems.add(cssItem);
188+
}
182189
if (phpItem != null) {
183190
newItems.add(phpItem);
184191
}
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.netbeans.modules.php.editor.embedding;
21+
22+
import java.awt.Color;
23+
import java.util.Collection;
24+
import java.util.List;
25+
import java.util.NoSuchElementException;
26+
import java.util.logging.Level;
27+
import java.util.logging.Logger;
28+
import javax.swing.text.AttributeSet;
29+
import javax.swing.text.Document;
30+
import javax.swing.text.SimpleAttributeSet;
31+
import javax.swing.text.StyleConstants;
32+
import org.netbeans.api.editor.mimelookup.MimeLookup;
33+
import org.netbeans.api.editor.settings.AttributesUtilities;
34+
import org.netbeans.api.editor.settings.FontColorSettings;
35+
import org.netbeans.api.lexer.Language;
36+
import org.netbeans.api.lexer.LanguagePath;
37+
import org.netbeans.api.lexer.TokenHierarchy;
38+
import org.netbeans.api.lexer.TokenHierarchyEvent;
39+
import org.netbeans.api.lexer.TokenHierarchyListener;
40+
import org.netbeans.api.lexer.TokenSequence;
41+
import static org.netbeans.modules.php.api.util.FileUtils.PHP_MIME_TYPE;
42+
import org.netbeans.spi.editor.highlighting.HighlightsSequence;
43+
import org.netbeans.spi.editor.highlighting.support.AbstractHighlightsContainer;
44+
import org.openide.util.Lookup.Result;
45+
import org.openide.util.LookupEvent;
46+
import org.openide.util.LookupListener;
47+
import org.openide.util.WeakListeners;
48+
49+
/**
50+
* The class {@link org.netbeans.modules.html.editor.coloring.EmbeddingHighlightsContainer} is taken as a basis
51+
*/
52+
public class CssEmbeddingHighlightsContainer extends AbstractHighlightsContainer implements TokenHierarchyListener {
53+
54+
private static final Logger LOG = Logger.getLogger(CssEmbeddingHighlightsContainer.class.getName());
55+
56+
private static final String CSS_BACKGROUND_TOKEN_NAME = "css-embedding"; //NOI18N
57+
private static final String CSS_MIME_TYPE = "text/css"; //NOI18N
58+
59+
private final Document document;
60+
private AttributeSet cssBackground;
61+
private TokenHierarchy<? extends Document> hierarchy = null;
62+
private long version = 0;
63+
64+
private Result<FontColorSettings> lookupResult;
65+
private LookupListener lookupListener;
66+
67+
CssEmbeddingHighlightsContainer(Document document) {
68+
this.document = document;
69+
70+
lookupResult = MimeLookup.getLookup(PHP_MIME_TYPE).lookupResult(FontColorSettings.class);
71+
lookupResult.addLookupListener(WeakListeners.create(LookupListener.class,
72+
lookupListener = new LookupListener() {
73+
@Override
74+
public void resultChanged(LookupEvent ev) {
75+
refreshColorings();
76+
}
77+
}, lookupResult));
78+
refreshColorings();
79+
}
80+
81+
private void refreshColorings() {
82+
Collection<? extends FontColorSettings> allInstances = lookupResult.allInstances();
83+
assert allInstances.size() > 0;
84+
85+
FontColorSettings fcs = allInstances.iterator().next();
86+
Color cssBC = null;
87+
if (fcs != null) {
88+
cssBC = getColoring(fcs, CSS_BACKGROUND_TOKEN_NAME);
89+
}
90+
91+
cssBackground = cssBC == null ? SimpleAttributeSet.EMPTY : AttributesUtilities.createImmutable(
92+
StyleConstants.Background, cssBC,
93+
ATTR_EXTENDS_EOL, Boolean.TRUE);
94+
}
95+
96+
@Override
97+
public HighlightsSequence getHighlights(int startOffset, int endOffset) {
98+
synchronized (this) {
99+
if (cssBackground != null) {
100+
if (hierarchy == null) {
101+
hierarchy = TokenHierarchy.get(document);
102+
if (hierarchy != null) {
103+
hierarchy.addTokenHierarchyListener(WeakListeners.create(TokenHierarchyListener.class, this, hierarchy));
104+
}
105+
}
106+
107+
if (hierarchy != null) {
108+
return new Highlights(version, hierarchy, startOffset, endOffset);
109+
}
110+
}
111+
return HighlightsSequence.EMPTY;
112+
}
113+
}
114+
115+
// ----------------------------------------------------------------------
116+
// TokenHierarchyListener implementation
117+
// ----------------------------------------------------------------------
118+
119+
@Override
120+
public void tokenHierarchyChanged(TokenHierarchyEvent evt) {
121+
synchronized (this) {
122+
version++;
123+
}
124+
125+
fireHighlightsChange(evt.affectedStartOffset(), evt.affectedEndOffset());
126+
}
127+
128+
// ----------------------------------------------------------------------
129+
// Private implementation
130+
// ----------------------------------------------------------------------
131+
132+
private static Color getColoring(FontColorSettings fcs, String tokenName) {
133+
AttributeSet as = fcs.getTokenFontColors(tokenName);
134+
if (as != null) {
135+
return (Color) as.getAttribute(StyleConstants.Background); //NOI18N
136+
}
137+
return null;
138+
}
139+
140+
private class Highlights implements HighlightsSequence {
141+
142+
private final long version;
143+
private final TokenHierarchy<? extends Document> scanner;
144+
private final int startOffsetBoundary;
145+
private final int endOffsetBoundary;
146+
147+
private List<TokenSequence<?>> tokenSequenceList = null;
148+
private int startOffset;
149+
private int endOffset;
150+
private int realEndOffset;
151+
private AttributeSet attributeSet;
152+
private boolean finished = false;
153+
154+
private Highlights(long version, TokenHierarchy<? extends Document> scanner, int startOffset, int endOffset) {
155+
this.version = version;
156+
this.scanner = scanner;
157+
this.startOffsetBoundary = startOffset;
158+
this.endOffsetBoundary = endOffset;
159+
}
160+
161+
private boolean _moveNext() {
162+
if (tokenSequenceList == null) {
163+
// initialize
164+
this.startOffset = startOffsetBoundary;
165+
this.endOffset = startOffsetBoundary;
166+
this.realEndOffset = startOffsetBoundary;
167+
168+
String mimeType = (String) document.getProperty ("mimeType"); //NOI18N
169+
Language<?> language = Language.find(mimeType);
170+
if (language != null) {
171+
//get php token sequence list
172+
LanguagePath topLevelLanguagePath = LanguagePath.get(language);
173+
tokenSequenceList = scanner.tokenSequenceList(topLevelLanguagePath, startOffsetBoundary, endOffsetBoundary);
174+
} else {
175+
LOG.log(Level.WARNING, "Language " + mimeType + " obtained from the document mimeType property cannot be found!"); //NOI18N
176+
}
177+
}
178+
179+
if (tokenSequenceList != null) {
180+
for (TokenSequence tokenSequence : tokenSequenceList) {
181+
assert tokenSequence.language().mimeType().equals(PHP_MIME_TYPE);
182+
tokenSequence.move(realEndOffset);
183+
while (tokenSequence.moveNext() && tokenSequence.offset() < endOffsetBoundary) {
184+
TokenSequence eTokenSequence = tokenSequence.embedded();
185+
186+
if (eTokenSequence == null || !eTokenSequence.moveNext()) {
187+
continue;
188+
}
189+
190+
String embeddedMimeType = eTokenSequence.language().mimeType();
191+
if ((CSS_MIME_TYPE).equals(embeddedMimeType)) {
192+
eTokenSequence.move(realEndOffset);
193+
if(eTokenSequence.moveNext()) {
194+
startOffset = eTokenSequence.offset();
195+
do {
196+
endOffset = eTokenSequence.offset() + eTokenSequence.token().length();
197+
} while (eTokenSequence.moveNext());
198+
realEndOffset = endOffset > realEndOffset ? endOffset : realEndOffset + 1;
199+
200+
attributeSet = cssBackground;
201+
if (attributeSet != null) {
202+
return true;
203+
}
204+
}
205+
}
206+
}
207+
attributeSet = cssBackground;
208+
if (attributeSet != null) {
209+
return true;
210+
}
211+
}
212+
}
213+
214+
return false;
215+
}
216+
217+
@Override
218+
public boolean moveNext() {
219+
synchronized (CssEmbeddingHighlightsContainer.this) {
220+
if (checkVersion()) {
221+
if (_moveNext()) {
222+
return true;
223+
}
224+
}
225+
}
226+
227+
finished = true;
228+
return false;
229+
}
230+
231+
@Override
232+
public int getStartOffset() {
233+
synchronized (CssEmbeddingHighlightsContainer.this) {
234+
if (finished) {
235+
throw new NoSuchElementException();
236+
} else {
237+
assert tokenSequenceList != null : "Sequence not initialized, call moveNext() first."; //NOI18N
238+
return startOffset;
239+
}
240+
}
241+
}
242+
243+
@Override
244+
public int getEndOffset() {
245+
synchronized (CssEmbeddingHighlightsContainer.this) {
246+
if (finished) {
247+
throw new NoSuchElementException();
248+
} else {
249+
assert tokenSequenceList != null : "Sequence not initialized, call moveNext() first."; //NOI18N
250+
return endOffset;
251+
}
252+
}
253+
}
254+
255+
@Override
256+
public AttributeSet getAttributes() {
257+
synchronized (CssEmbeddingHighlightsContainer.this) {
258+
if (finished) {
259+
throw new NoSuchElementException();
260+
} else {
261+
assert tokenSequenceList != null : "Sequence not initialized, call moveNext() first."; //NOI18N
262+
return attributeSet;
263+
}
264+
}
265+
}
266+
267+
private boolean checkVersion() {
268+
return this.version == CssEmbeddingHighlightsContainer.this.version;
269+
}
270+
}
271+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.netbeans.modules.php.editor.embedding;
20+
21+
import java.util.ArrayList;
22+
import java.util.Collections;
23+
import java.util.List;
24+
import org.netbeans.api.lexer.Token;
25+
import org.netbeans.api.lexer.TokenHierarchy;
26+
import org.netbeans.api.lexer.TokenId;
27+
import org.netbeans.api.lexer.TokenSequence;
28+
import org.netbeans.modules.parsing.api.Embedding;
29+
import org.netbeans.modules.parsing.api.Snapshot;
30+
import org.netbeans.modules.parsing.spi.EmbeddingProvider;
31+
import static org.netbeans.modules.php.api.util.FileUtils.PHP_MIME_TYPE;
32+
import static org.netbeans.modules.php.editor.embedding.CssEmbeddingProvider.TARGET_MIME_TYPE;
33+
import static org.netbeans.modules.php.editor.lexer.PHPTokenId.T_EMBEDDED_CSS;
34+
35+
/**
36+
* This class is a copy of the {@link org.netbeans.modules.javascript2.vue.editor.embedding.VueCssEmbeddingProvider}
37+
* with minor changes
38+
*/
39+
@EmbeddingProvider.Registration(
40+
mimeType = PHP_MIME_TYPE,
41+
targetMimeType = TARGET_MIME_TYPE)
42+
public class CssEmbeddingProvider extends EmbeddingProvider {
43+
44+
public static final String TARGET_MIME_TYPE = "text/css"; //NOI18N
45+
46+
private volatile boolean cancelled = true;
47+
48+
@Override
49+
public List<Embedding> getEmbeddings(Snapshot snapshot) {
50+
cancelled = false;
51+
TokenHierarchy<?> tokenHierarchy = snapshot.getTokenHierarchy();
52+
TokenSequence<?> ts = tokenHierarchy.tokenSequence();
53+
54+
if (ts == null || !ts.isValid()) {
55+
return Collections.emptyList();
56+
}
57+
58+
ts.moveStart();
59+
60+
List<Embedding> embeddings = new ArrayList<>();
61+
62+
while (ts.moveNext()) {
63+
if (cancelled) {
64+
embeddings.clear();
65+
break;
66+
}
67+
Token<?> token = ts.token();
68+
TokenId id = token.id();
69+
if (id.equals(T_EMBEDDED_CSS)) {
70+
embeddings.add(snapshot.create(ts.offset(), token.length(), TARGET_MIME_TYPE));
71+
}
72+
}
73+
74+
if (embeddings.isEmpty()) {
75+
return Collections.emptyList();
76+
}
77+
return Collections.singletonList(Embedding.create(embeddings));
78+
}
79+
80+
@Override
81+
public int getPriority() {
82+
return 200;
83+
}
84+
85+
@Override
86+
public void cancel() {
87+
cancelled = true;
88+
}
89+
}

0 commit comments

Comments
 (0)