mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-23 08:55:25 +02:00
Bug 481126 - QML Imports Working in Editor
Hooked up the extra logic needed to get Tern-QML's imports to work in the QML File Editor. Change-Id: I6fa222223ca8b6b177e4004e48f2f1863ab4d7b4 Signed-off-by: Matthew Bastien <mbastien@blackberry.com>
This commit is contained in:
parent
3a2cb0431e
commit
42e3859b68
8 changed files with 186 additions and 18 deletions
|
@ -28,9 +28,7 @@ public class NashornTests {
|
|||
int pos = code.indexOf('|');
|
||||
code = code.substring(0, pos) + code.substring(pos + 1);
|
||||
|
||||
analyzer.addFile("test1.qml", code);
|
||||
|
||||
Collection<QMLTernCompletion> QMLTernCompletions = analyzer.getCompletions("test1.qml", pos);
|
||||
Collection<QMLTernCompletion> QMLTernCompletions = analyzer.getCompletions("test1.qml", code, pos);
|
||||
|
||||
Map<String, QMLTernCompletion> set = new HashMap<>();
|
||||
Set<String> unexpected = new HashSet<>();
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.io.FileNotFoundException;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
@ -24,6 +25,13 @@ import javax.script.ScriptEngineManager;
|
|||
import javax.script.ScriptException;
|
||||
|
||||
import org.eclipse.cdt.internal.qt.core.Activator;
|
||||
import org.eclipse.core.resources.IContainer;
|
||||
import org.eclipse.core.resources.IFolder;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IWorkspaceRoot;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.IPath;
|
||||
import org.eclipse.core.runtime.Path;
|
||||
|
||||
@SuppressWarnings("nls")
|
||||
public class QMLAnalyzer {
|
||||
|
@ -65,19 +73,51 @@ public class QMLAnalyzer {
|
|||
invoke.invokeMethod(defs, "push", engine.get("ecma5defs"));
|
||||
options.put("defs", defs);
|
||||
|
||||
ResolveDirectory resolveDirectory = (file, pathString) -> {
|
||||
String filename = (String) file.get("name");
|
||||
int slash = filename.lastIndexOf('/');
|
||||
String fileDirectory = slash >= 0 ? filename.substring(0, slash + 1) : filename;
|
||||
if (pathString == null) {
|
||||
return fileDirectory;
|
||||
}
|
||||
IPath path = Path.fromOSString(pathString);
|
||||
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
|
||||
if (!path.isAbsolute()) {
|
||||
IResource res = root.findMember(fileDirectory);
|
||||
if (res instanceof IContainer) {
|
||||
IContainer dir = (IContainer) res;
|
||||
res = dir.findMember(path);
|
||||
if (res != null) {
|
||||
String p = res.getFullPath().toString().substring(1);
|
||||
if (!p.isEmpty() && !p.endsWith("/")) {
|
||||
p += "/";
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pathString;
|
||||
};
|
||||
options.put("resolveDirectory", invoke.invokeFunction("resolveDirectory", resolveDirectory));
|
||||
|
||||
synchronized (this) {
|
||||
tern = invoke.invokeFunction("newTernServer", options);
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ResolveDirectory {
|
||||
public String resolveDirectory(Bindings file, String path);
|
||||
}
|
||||
|
||||
private Object load(String file) throws ScriptException, IOException {
|
||||
URL scriptURL = Activator.getDefault().getBundle().getEntry(file);
|
||||
if (scriptURL == null) {
|
||||
throw new FileNotFoundException(file);
|
||||
}
|
||||
engine.getContext().setAttribute(ScriptEngine.FILENAME, file, ScriptContext.ENGINE_SCOPE);
|
||||
return engine.eval(new BufferedReader(new InputStreamReader(scriptURL.openStream())));
|
||||
return engine.eval(new BufferedReader(new InputStreamReader(scriptURL.openStream(), StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
private void waitUntilLoaded() {
|
||||
|
@ -93,8 +133,9 @@ public class QMLAnalyzer {
|
|||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface RequestCallback {
|
||||
void callback(Object err, Object data);
|
||||
void callback(Object err, Bindings data);
|
||||
}
|
||||
|
||||
public void addFile(String fileName, String code) throws NoSuchMethodException, ScriptException {
|
||||
|
@ -102,9 +143,21 @@ public class QMLAnalyzer {
|
|||
invoke.invokeMethod(tern, "addFile", fileName, code);
|
||||
}
|
||||
|
||||
public Collection<QMLTernCompletion> getCompletions(String fileName, int pos)
|
||||
public void deleteFile(String fileName) throws NoSuchMethodException, ScriptException {
|
||||
waitUntilLoaded();
|
||||
invoke.invokeMethod(tern, "delFile", fileName);
|
||||
}
|
||||
|
||||
public Collection<QMLTernCompletion> getCompletions(String fileName, String text, int pos)
|
||||
throws NoSuchMethodException, ScriptException {
|
||||
waitUntilLoaded();
|
||||
Bindings file = engine.createBindings();
|
||||
file.put("type", "full");
|
||||
file.put("name", fileName);
|
||||
file.put("text", text);
|
||||
Bindings files = (Bindings) engine.eval("new Array()");
|
||||
invoke.invokeMethod(files, "push", file);
|
||||
|
||||
Bindings query = engine.createBindings();
|
||||
query.put("type", "completions");
|
||||
query.put("file", fileName);
|
||||
|
@ -119,6 +172,7 @@ public class QMLAnalyzer {
|
|||
query.put("includeKeywords", true);
|
||||
query.put("guess", false);
|
||||
Bindings request = engine.createBindings();
|
||||
request.put("files", files);
|
||||
request.put("query", query);
|
||||
|
||||
List<QMLTernCompletion> completions = new ArrayList<>();
|
||||
|
@ -129,7 +183,7 @@ public class QMLAnalyzer {
|
|||
} else {
|
||||
try {
|
||||
for (Bindings completion : (Bindings[]) invoke.invokeMethod(engine.get("Java"), "to",
|
||||
((Bindings) data).get("completions"), "javax.script.Bindings[]")) {
|
||||
data.get("completions"), "javax.script.Bindings[]")) {
|
||||
completions.add(new QMLTernCompletion((String) completion.get("name"),
|
||||
(String) completion.get("type"), (String) completion.get("origin")));
|
||||
}
|
||||
|
|
|
@ -2,8 +2,14 @@ function newTernServer(options) {
|
|||
return new tern.Server(options);
|
||||
}
|
||||
|
||||
function requestCallback(obj) {
|
||||
return function(err, data) {
|
||||
obj.callback(err, data);
|
||||
}
|
||||
function resolveDirectory(obj) {
|
||||
return function (file, path) {
|
||||
return obj.resolveDirectory(file, path);
|
||||
};
|
||||
}
|
||||
|
||||
function requestCallback(obj) {
|
||||
return function (err, data) {
|
||||
obj.callback(err, data);
|
||||
};
|
||||
}
|
|
@ -57,7 +57,7 @@
|
|||
dir = dir.substring(0, dir.lastIndexOf("/") + 1);
|
||||
return dir;
|
||||
}
|
||||
if (path.startsWith("./")) {
|
||||
if (path.substring(0, 2) === "./") {
|
||||
path = file.directory + path.substring(2);
|
||||
}
|
||||
if (path.substr(path.length - 1, 1) !== "/") {
|
||||
|
|
|
@ -10,12 +10,18 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.cdt.internal.qt.ui.editor;
|
||||
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import org.eclipse.cdt.internal.qt.ui.Activator;
|
||||
import org.eclipse.cdt.internal.qt.ui.text.QMLSourceViewerConfiguration;
|
||||
import org.eclipse.cdt.qt.core.QMLAnalyzer;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.jface.preference.IPreferenceStore;
|
||||
import org.eclipse.jface.text.IDocument;
|
||||
import org.eclipse.jface.text.IDocumentExtension3;
|
||||
import org.eclipse.jface.text.source.DefaultCharacterPairMatcher;
|
||||
import org.eclipse.jface.text.source.ICharacterPairMatcher;
|
||||
import org.eclipse.ui.IFileEditorInput;
|
||||
import org.eclipse.ui.editors.text.TextEditor;
|
||||
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
|
||||
|
||||
|
@ -29,6 +35,7 @@ public class QMLEditor extends TextEditor {
|
|||
private static final String BRACKET_MATCHING_PREFERENCE = "org.eclipse.cdt.qt.ui.qmlMatchingBrackets"; //$NON-NLS-1$
|
||||
|
||||
private static final char[] BRACKETS = { '{', '}', '(', ')', '[', ']' };
|
||||
private final QMLAnalyzer analyzer = Activator.getService(QMLAnalyzer.class);
|
||||
|
||||
@Override
|
||||
protected void initializeEditor() {
|
||||
|
@ -36,6 +43,23 @@ public class QMLEditor extends TextEditor {
|
|||
setSourceViewerConfiguration(new QMLSourceViewerConfiguration(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doSave(IProgressMonitor progressMonitor) {
|
||||
IFileEditorInput fileInput = (IFileEditorInput) getEditorInput();
|
||||
String fileName = fileInput.getFile().getFullPath().toString().substring(1);
|
||||
IDocument document = getSourceViewer().getDocument();
|
||||
|
||||
try {
|
||||
analyzer.deleteFile(fileName);
|
||||
analyzer.addFile(fileName, document.get());
|
||||
} catch (NoSuchMethodException e) {
|
||||
Activator.log(e);
|
||||
} catch (ScriptException e) {
|
||||
Activator.log(e);
|
||||
}
|
||||
super.doSave(progressMonitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {
|
||||
super.configureSourceViewerDecorationSupport(support);
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2015 QNX Software Systems 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
|
||||
*
|
||||
* Contributors:
|
||||
* QNX Software Systems - Initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.internal.qt.ui.resources;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import org.eclipse.cdt.internal.qt.ui.Activator;
|
||||
import org.eclipse.cdt.qt.core.QMLAnalyzer;
|
||||
import org.eclipse.core.resources.IFile;
|
||||
import org.eclipse.core.resources.IResource;
|
||||
import org.eclipse.core.resources.IResourceDelta;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
import org.eclipse.core.runtime.jobs.Job;
|
||||
|
||||
public class QMLTernFileUpdateJob extends Job {
|
||||
|
||||
private List<IResourceDelta> deltaList;
|
||||
private final QMLAnalyzer analyzer = Activator.getService(QMLAnalyzer.class);
|
||||
|
||||
public QMLTernFileUpdateJob(List<IResourceDelta> deltas) {
|
||||
super("Add/Remove Files in Tern"); //$NON-NLS-1$
|
||||
this.deltaList = deltas;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IStatus run(IProgressMonitor monitor) {
|
||||
for (IResourceDelta delta : deltaList) {
|
||||
IResource resource = delta.getResource();
|
||||
String fileName = resource.getFullPath().toString().substring(1);
|
||||
|
||||
if (resource instanceof IFile) {
|
||||
IFile file = (IFile) resource;
|
||||
try {
|
||||
if ((delta.getKind() & IResourceDelta.ADDED) > 0) {
|
||||
analyzer.addFile(fileName, readFileContents(file.getContents()));
|
||||
} else if ((delta.getKind() & IResourceDelta.REMOVED) > 0) {
|
||||
analyzer.deleteFile(fileName);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
Activator.log(e);
|
||||
} catch (ScriptException e) {
|
||||
Activator.log(e);
|
||||
} catch (IOException e) {
|
||||
Activator.log(e);
|
||||
} catch (CoreException e) {
|
||||
Activator.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status.OK_STATUS;
|
||||
}
|
||||
|
||||
private String readFileContents(InputStream stream) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int read;
|
||||
while ((read = stream.read()) != -1) {
|
||||
sb.append((char) read);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@ public class QtResourceChangeListener implements IResourceChangeListener {
|
|||
}
|
||||
|
||||
final List<IResourceDelta> deltaList = new ArrayList<>();
|
||||
final List<IResourceDelta> qmlDeltaList = new ArrayList<>();
|
||||
IResourceDeltaVisitor visitor = new IResourceDeltaVisitor() {
|
||||
|
||||
@Override
|
||||
|
@ -79,8 +80,13 @@ public class QtResourceChangeListener implements IResourceChangeListener {
|
|||
return false;
|
||||
}
|
||||
|
||||
// We don't care about resources that have simply been updated
|
||||
if ((delta.getKind() & IResourceDelta.CHANGED) > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We only care about added and removed resources at this point
|
||||
if ((delta.getKind() & (IResourceDelta.ADDED | IResourceDelta.REMOVED)) == 0) {
|
||||
if ((delta.getKind() & IResourceDelta.ADDED | IResourceDelta.REMOVED) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -91,6 +97,8 @@ public class QtResourceChangeListener implements IResourceChangeListener {
|
|||
// Project. Add it to the list of deltas so we can update
|
||||
// the project file later.
|
||||
deltaList.add(delta);
|
||||
} else if ("qml".equals(resource.getFileExtension())) { //$NON-NLS-1$
|
||||
qmlDeltaList.add(delta);
|
||||
}
|
||||
|
||||
// Doesn't really matter since this line can only be reached if
|
||||
|
@ -111,5 +119,9 @@ public class QtResourceChangeListener implements IResourceChangeListener {
|
|||
if (!deltaList.isEmpty()) {
|
||||
new QtProjectFileUpdateJob(deltaList).schedule();
|
||||
}
|
||||
// Schedule the job to update the tern server with added/deleted qml files
|
||||
if (!qmlDeltaList.isEmpty()) {
|
||||
new QMLTernFileUpdateJob(qmlDeltaList).schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,12 +42,11 @@ public class QMLContentAssistProcessor implements IContentAssistProcessor {
|
|||
String prefix = lastWord(document, offset);
|
||||
// Save the file
|
||||
IFileEditorInput fileInput = (IFileEditorInput) editor.getEditorInput();
|
||||
String fileName = fileInput.getFile().getName();// getLocation().toOSString().substring(1);
|
||||
String fileName = fileInput.getFile().getFullPath().toString().substring(1);// getLocation().toOSString().substring(1);
|
||||
|
||||
try {
|
||||
String contents = document.get();
|
||||
analyzer.addFile(fileName, contents);
|
||||
Collection<QMLTernCompletion> completions = analyzer.getCompletions(fileName, offset);
|
||||
Collection<QMLTernCompletion> completions = analyzer.getCompletions(fileName, contents, offset);
|
||||
if (!completions.isEmpty()) {
|
||||
ICompletionProposal[] proposals = new ICompletionProposal[completions.size()];
|
||||
int i = 0;
|
||||
|
@ -64,8 +63,7 @@ public class QMLContentAssistProcessor implements IContentAssistProcessor {
|
|||
return proposals;
|
||||
}
|
||||
} catch (NoSuchMethodException | ScriptException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
Activator.log(e);
|
||||
}
|
||||
return NO_COMPLETIONS;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue