org.netbeans.modules.csl.hints.GsfHintsProvider Maven / Gradle / Ivy
The newest version!
/*
* 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.csl.hints;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.swing.text.Document;
import javax.swing.text.StyledDocument;
import org.netbeans.modules.csl.api.Error;
import org.netbeans.modules.parsing.spi.Scheduler;
import org.netbeans.spi.editor.hints.Fix;
import org.netbeans.spi.editor.hints.LazyFixList;
import org.netbeans.spi.editor.hints.Severity;
import org.openide.filesystems.FileObject;
import java.util.EnumMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.text.BadLocationException;
import javax.swing.text.Position;
import javax.swing.text.Position.Bias;
import org.netbeans.modules.csl.core.Language;
import org.netbeans.modules.csl.api.HintsProvider;
import org.netbeans.modules.csl.core.LanguageRegistry;
import org.netbeans.modules.csl.api.DataLoadersBridge;
import org.netbeans.modules.csl.api.Hint;
import org.netbeans.modules.csl.api.OffsetRange;
import org.netbeans.modules.csl.api.RuleContext;
import org.netbeans.modules.csl.core.CancelSupportImplementation;
import org.netbeans.modules.csl.core.SchedulerTaskCancelSupportImpl;
import org.netbeans.modules.csl.core.SpiSupportAccessor;
import org.netbeans.modules.csl.hints.infrastructure.GsfHintsManager;
import org.netbeans.modules.csl.spi.ParserResult;
import org.netbeans.modules.parsing.api.Embedding;
import org.netbeans.modules.parsing.api.ParserManager;
import org.netbeans.modules.parsing.api.ResultIterator;
import org.netbeans.modules.parsing.api.Snapshot;
import org.netbeans.modules.parsing.api.UserTask;
import org.netbeans.modules.parsing.spi.ParseException;
import org.netbeans.modules.parsing.spi.ParserResultTask;
import org.netbeans.modules.parsing.spi.SchedulerEvent;
import org.netbeans.spi.editor.hints.ErrorDescription;
import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
import org.netbeans.spi.editor.hints.HintsController;
import org.openide.text.NbDocument;
/**
* This class is based on JavaHintsFactory in Retouche's org.netbeans.modules.java.hints
* This file is originally from Retouche, the Java Support
* infrastructure in NetBeans. I have modified the file as little
* as possible to make merging Retouche fixes back as simple as
* possible.
*
*
* @author Jan Lahoda
* @author leon chiver
* @author Tor Norbye
*/
public final class GsfHintsProvider extends ParserResultTask {
public static final Logger LOG = Logger.getLogger(GsfHintsProvider.class.getName()); // NOI18N
private final CancelSupportImplementation cancel = SchedulerTaskCancelSupportImpl.create(this);
private FileObject file;
/**
* Tracks the HintsProvider being executed, so it can be cancelled.
*/
private volatile HintsProvider pendingProvider;
/**
* Creates a new instance of GsfHintsProvider
*/
GsfHintsProvider(FileObject file) {
this.file = file;
}
private static final Map errorKind2Severity;
static {
errorKind2Severity = new EnumMap(org.netbeans.modules.csl.api.Severity.class);
errorKind2Severity.put(org.netbeans.modules.csl.api.Severity.ERROR, Severity.ERROR);
errorKind2Severity.put(org.netbeans.modules.csl.api.Severity.WARNING, Severity.WARNING);
errorKind2Severity.put(org.netbeans.modules.csl.api.Severity.INFO, Severity.HINT);
// errorKind2Severity.put(Error/*Diagnostic*/.Kind.NOTE, Severity.WARNING);
// errorKind2Severity.put(Error/*Diagnostic*/.Kind.OTHER, Severity.WARNING);
}
List computeErrors(Document doc, ParserResult result, List extends Error> errors, List descs) {
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "errors = " + errors);
}
for (Error d : errors) {
if (cancel.isCancelled()) {
return null;
}
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "d = " + d);
//Map> code2Rules = RulesManager.getInstance().getErrors();
}
//Map> code2Rules = RulesManager.getInstance().getErrors();
//List rules = code2Rules.get(d.getKey());
//if (LOG.isLoggable(Level.FINE)) {
//LOG.log(Level.FINE, "code= " + d.getKey());
//LOG.log(Level.FINE, "rules = " + rules);
//}
//int position = (int)d.getPosition();
int astOffset = d.getStartPosition();
int astEndOffset = d.getEndPosition();
int position, endPosition;
position = result.getSnapshot().getOriginalOffset(astOffset);
if (position == -1) {
continue;
}
endPosition = position+(astEndOffset-astOffset);
LazyFixList ehm;
//if (rules != null) {
// ehm = new CreatorBasedLazyFixList(info.getFileObject(), d.getKey(), (int)getPrefferedPosition(info, d), rules, data);
//} else {
ehm = ErrorDescriptionFactory.lazyListForFixes(Collections.emptyList());
//}
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "ehm=" + ehm);
}
final String desc = d.getDisplayName();
final Position[] range = getLine(d, doc, position, endPosition);
if (cancel.isCancelled()) {
return null;
}
if (range[0] == null || range[1] == null) {
continue;
}
descs.add(ErrorDescriptionFactory.createErrorDescription(errorKind2Severity.get(d.getSeverity()), desc, ehm, doc, range[0], range[1]));
}
if (cancel.isCancelled()) {
return null;
}
return descs;
}
public Document getDocument() {
return DataLoadersBridge.getDefault().getDocument(file);
}
private Position[] getLine(final Error d, final Document doc, final int startOffset, final int endOffset) {
if (doc == null) {
return new Position[2];
}
final Position[][] ret = new Position[][] { new Position[2] };
// line nunmber conversion + getText from the line should happen
// under the document read-lock.
doc.render(new Runnable() {
public void run() {
if (cancel.isCancelled()) {
return;
}
ret[0] = getLine0(d, doc, startOffset, endOffset);
}
});
return ret[0];
}
private Position[] getLine0(Error d, final Document doc, int startOffset, int endOffset) {
StyledDocument sdoc = (StyledDocument) doc;
int lineNumber = NbDocument.findLineNumber(sdoc, startOffset);
int lineOffset = NbDocument.findLineOffset(sdoc, lineNumber);
String text = DataLoadersBridge.getDefault().getLine(doc, lineNumber);
if (text == null) {
return new Position[2];
}
if (d.isLineError()) {
int column = 0;
int length = text.length();
while (column < text.length() && Character.isWhitespace(text.charAt(column))) {
column++;
}
while (length > 0 && Character.isWhitespace(text.charAt(length - 1))) {
length--;
}
startOffset = lineOffset + column;
endOffset = lineOffset + length;
if (startOffset > endOffset) {
// Space only on the line
startOffset = lineOffset;
}
}
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "startOffset = " + startOffset );
LOG.log(Level.FINE, "endOffset = " + endOffset );
}
final int startOffsetFinal = startOffset;
final int endOffsetFinal = endOffset;
final Position[] result = new Position[2];
int len = doc.getLength();
if (startOffsetFinal > len || endOffsetFinal > len) {
if (!cancel.isCancelled() && LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "document changed, but not canceled?" );
LOG.log(Level.WARNING, "len = " + len );
LOG.log(Level.WARNING, "startOffset = " + startOffsetFinal );
LOG.log(Level.WARNING, "endOffset = " + endOffsetFinal );
}
cancel();
return result;
}
try {
result[0] = NbDocument.createPosition(doc, startOffsetFinal, Bias.Forward);
result[1] = NbDocument.createPosition(doc, endOffsetFinal, Bias.Backward);
} catch (BadLocationException e) {
LOG.log(Level.WARNING, null, e);
}
return result;
}
@Override
public void cancel() {
final HintsProvider curProvider = this.pendingProvider;
if (curProvider != null) {
curProvider.cancel();
}
}
private List processProviderErrors(
final List descriptions,
Snapshot topLevelSnapshot, final ParserResult r, final Language language) throws ParseException {
HintsProvider provider = language.getHintsProvider();
if (provider == null) {
return null;
}
GsfHintsManager manager = language.getHintsManager();
if (manager == null) {
return null;
}
RuleContext ruleContext = manager.createRuleContext(r, language, -1, -1, -1);
if (ruleContext == null) {
return null;
}
List hints = new ArrayList();
List errors = new ArrayList();
try {
synchronized (this) {
pendingProvider = provider;
if (cancel.isCancelled()) {
return errors;
}
}
provider.computeErrors(manager, ruleContext, hints, errors);
} finally {
pendingProvider = null;
}
boolean allowDisableEmpty = true;
for (int i = 0; i < hints.size(); i++) {
Hint hint = hints.get(i);
OffsetRange range = hint.getRange();
if (range != null &&
range.getStart() >= 0 && range.getStart() <= topLevelSnapshot.getText().length() &&
range.getEnd() >= 0 && range.getEnd() <= topLevelSnapshot.getText().length() &&
range.getStart() <= range.getEnd()
) {
ErrorDescription errorDesc = manager.createDescription(hint, ruleContext, allowDisableEmpty, i == hints.size()-1);
descriptions.add(errorDesc);
} else {
String msg = provider + " supplied hint " + hint + " with invalid range " + range + //NOI18N
", topLevelSnapshot.length=" + topLevelSnapshot.getText().length() +
", file=" + topLevelSnapshot.getSource().getFileObject(); //NOI18N
// assert false : msg;
LOG.log(Level.FINE, msg);
}
}
return errors;
}
private void refreshErrors(final ResultIterator resultIterator) throws ParseException {
List descs = new ArrayList();
Document doc = getDocument();
processErrorsRecursive(resultIterator, doc, descs, resultIterator.getSnapshot());
HintsController.setErrors(doc, GsfHintsFactory.LAYER_NAME, descs);
}
private void processErrorsRecursive(final ResultIterator resultIterator,
Document doc,
final List descriptions,
Snapshot topLevelSnapshot) throws ParseException {
if (resultIterator == null) {
return;
}
if (doc == null) {
doc = getDocument();
}
for(Embedding e : resultIterator.getEmbeddings()) {
try {
if (cancel.isCancelled()) {
return;
}
} catch (Exception ex) {
// should never happen, but log:
LOG.log(Level.WARNING, "Unexpected error", ex);
}
processErrorsRecursive(resultIterator.getResultIterator(e), doc, descriptions, topLevelSnapshot);
}
if (!(resultIterator.getParserResult() instanceof ParserResult)) {
return;
}
processErrors(resultIterator.getSnapshot(), (ParserResult)resultIterator.getParserResult(),
doc, descriptions, topLevelSnapshot);
}
void processErrors(final Snapshot snapshot, final ParserResult result,
Document doc,
final List descriptions,
Snapshot topLevelSnapshot) throws ParseException {
if (doc == null) {
doc = getDocument();
}
Language language = LanguageRegistry.getInstance().getLanguageByMimeType(snapshot.getMimeType());
if (language == null) {
return;
}
if(!(result instanceof ParserResult)) {
return ;
}
ParserResult r = (ParserResult)result;
List extends Error> errors = r.getDiagnostics();
List desc = new ArrayList();
List unhandled = processProviderErrors(descriptions, topLevelSnapshot, r, language);
if (unhandled != null) {
errors = unhandled;
}
// Process errors without codes
desc = computeErrors(doc, r, errors, desc);
if (desc == null) {
//meaning: cancelled
return;
}
descriptions.addAll(desc);
}
public @Override void run(ParserResult result, SchedulerEvent event) {
final Document doc = getDocument();
if (doc == null) {
LOG.log(Level.INFO, "SemanticHighlighter: Cannot get document!");
return ;
}
SpiSupportAccessor.getInstance().setCancelSupport(cancel);
try {
ParserManager.parse(Collections.singleton(result.getSnapshot().getSource()), new UserTask() {
public @Override void run(ResultIterator resultIterator) throws ParseException {
refreshErrors(resultIterator);
}
});
} catch (ParseException e) {
LOG.log(Level.WARNING, null, e);
} finally {
SpiSupportAccessor.getInstance().removeCancelSupport(cancel);
}
}
public static void refreshErrors() {
}
@Override
public int getPriority() {
return Integer.MAX_VALUE;
}
@Override
public Class extends Scheduler> getSchedulerClass() {
return Scheduler.EDITOR_SENSITIVE_TASK_SCHEDULER;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy