mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-09-03 05:33:33 +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('|');
|
int pos = code.indexOf('|');
|
||||||
code = code.substring(0, pos) + code.substring(pos + 1);
|
code = code.substring(0, pos) + code.substring(pos + 1);
|
||||||
|
|
||||||
analyzer.addFile("test1.qml", code);
|
Collection<QMLTernCompletion> QMLTernCompletions = analyzer.getCompletions("test1.qml", code, pos);
|
||||||
|
|
||||||
Collection<QMLTernCompletion> QMLTernCompletions = analyzer.getCompletions("test1.qml", pos);
|
|
||||||
|
|
||||||
Map<String, QMLTernCompletion> set = new HashMap<>();
|
Map<String, QMLTernCompletion> set = new HashMap<>();
|
||||||
Set<String> unexpected = new HashSet<>();
|
Set<String> unexpected = new HashSet<>();
|
||||||
|
|
|
@ -12,6 +12,7 @@ import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -24,6 +25,13 @@ import javax.script.ScriptEngineManager;
|
||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
|
|
||||||
import org.eclipse.cdt.internal.qt.core.Activator;
|
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")
|
@SuppressWarnings("nls")
|
||||||
public class QMLAnalyzer {
|
public class QMLAnalyzer {
|
||||||
|
@ -65,19 +73,51 @@ public class QMLAnalyzer {
|
||||||
invoke.invokeMethod(defs, "push", engine.get("ecma5defs"));
|
invoke.invokeMethod(defs, "push", engine.get("ecma5defs"));
|
||||||
options.put("defs", defs);
|
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) {
|
synchronized (this) {
|
||||||
tern = invoke.invokeFunction("newTernServer", options);
|
tern = invoke.invokeFunction("newTernServer", options);
|
||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ResolveDirectory {
|
||||||
|
public String resolveDirectory(Bindings file, String path);
|
||||||
|
}
|
||||||
|
|
||||||
private Object load(String file) throws ScriptException, IOException {
|
private Object load(String file) throws ScriptException, IOException {
|
||||||
URL scriptURL = Activator.getDefault().getBundle().getEntry(file);
|
URL scriptURL = Activator.getDefault().getBundle().getEntry(file);
|
||||||
if (scriptURL == null) {
|
if (scriptURL == null) {
|
||||||
throw new FileNotFoundException(file);
|
throw new FileNotFoundException(file);
|
||||||
}
|
}
|
||||||
engine.getContext().setAttribute(ScriptEngine.FILENAME, file, ScriptContext.ENGINE_SCOPE);
|
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() {
|
private void waitUntilLoaded() {
|
||||||
|
@ -93,8 +133,9 @@ public class QMLAnalyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
public interface RequestCallback {
|
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 {
|
public void addFile(String fileName, String code) throws NoSuchMethodException, ScriptException {
|
||||||
|
@ -102,9 +143,21 @@ public class QMLAnalyzer {
|
||||||
invoke.invokeMethod(tern, "addFile", fileName, code);
|
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 {
|
throws NoSuchMethodException, ScriptException {
|
||||||
waitUntilLoaded();
|
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();
|
Bindings query = engine.createBindings();
|
||||||
query.put("type", "completions");
|
query.put("type", "completions");
|
||||||
query.put("file", fileName);
|
query.put("file", fileName);
|
||||||
|
@ -119,6 +172,7 @@ public class QMLAnalyzer {
|
||||||
query.put("includeKeywords", true);
|
query.put("includeKeywords", true);
|
||||||
query.put("guess", false);
|
query.put("guess", false);
|
||||||
Bindings request = engine.createBindings();
|
Bindings request = engine.createBindings();
|
||||||
|
request.put("files", files);
|
||||||
request.put("query", query);
|
request.put("query", query);
|
||||||
|
|
||||||
List<QMLTernCompletion> completions = new ArrayList<>();
|
List<QMLTernCompletion> completions = new ArrayList<>();
|
||||||
|
@ -129,7 +183,7 @@ public class QMLAnalyzer {
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
for (Bindings completion : (Bindings[]) invoke.invokeMethod(engine.get("Java"), "to",
|
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"),
|
completions.add(new QMLTernCompletion((String) completion.get("name"),
|
||||||
(String) completion.get("type"), (String) completion.get("origin")));
|
(String) completion.get("type"), (String) completion.get("origin")));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,14 @@ function newTernServer(options) {
|
||||||
return new tern.Server(options);
|
return new tern.Server(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function requestCallback(obj) {
|
function resolveDirectory(obj) {
|
||||||
return function(err, data) {
|
return function (file, path) {
|
||||||
obj.callback(err, data);
|
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);
|
dir = dir.substring(0, dir.lastIndexOf("/") + 1);
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
if (path.startsWith("./")) {
|
if (path.substring(0, 2) === "./") {
|
||||||
path = file.directory + path.substring(2);
|
path = file.directory + path.substring(2);
|
||||||
}
|
}
|
||||||
if (path.substr(path.length - 1, 1) !== "/") {
|
if (path.substr(path.length - 1, 1) !== "/") {
|
||||||
|
|
|
@ -10,12 +10,18 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.eclipse.cdt.internal.qt.ui.editor;
|
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.Activator;
|
||||||
import org.eclipse.cdt.internal.qt.ui.text.QMLSourceViewerConfiguration;
|
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.preference.IPreferenceStore;
|
||||||
|
import org.eclipse.jface.text.IDocument;
|
||||||
import org.eclipse.jface.text.IDocumentExtension3;
|
import org.eclipse.jface.text.IDocumentExtension3;
|
||||||
import org.eclipse.jface.text.source.DefaultCharacterPairMatcher;
|
import org.eclipse.jface.text.source.DefaultCharacterPairMatcher;
|
||||||
import org.eclipse.jface.text.source.ICharacterPairMatcher;
|
import org.eclipse.jface.text.source.ICharacterPairMatcher;
|
||||||
|
import org.eclipse.ui.IFileEditorInput;
|
||||||
import org.eclipse.ui.editors.text.TextEditor;
|
import org.eclipse.ui.editors.text.TextEditor;
|
||||||
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
|
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 String BRACKET_MATCHING_PREFERENCE = "org.eclipse.cdt.qt.ui.qmlMatchingBrackets"; //$NON-NLS-1$
|
||||||
|
|
||||||
private static final char[] BRACKETS = { '{', '}', '(', ')', '[', ']' };
|
private static final char[] BRACKETS = { '{', '}', '(', ')', '[', ']' };
|
||||||
|
private final QMLAnalyzer analyzer = Activator.getService(QMLAnalyzer.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initializeEditor() {
|
protected void initializeEditor() {
|
||||||
|
@ -36,6 +43,23 @@ public class QMLEditor extends TextEditor {
|
||||||
setSourceViewerConfiguration(new QMLSourceViewerConfiguration(this));
|
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
|
@Override
|
||||||
protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {
|
protected void configureSourceViewerDecorationSupport(SourceViewerDecorationSupport support) {
|
||||||
super.configureSourceViewerDecorationSupport(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> deltaList = new ArrayList<>();
|
||||||
|
final List<IResourceDelta> qmlDeltaList = new ArrayList<>();
|
||||||
IResourceDeltaVisitor visitor = new IResourceDeltaVisitor() {
|
IResourceDeltaVisitor visitor = new IResourceDeltaVisitor() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -79,8 +80,13 @@ public class QtResourceChangeListener implements IResourceChangeListener {
|
||||||
return false;
|
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
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +97,8 @@ public class QtResourceChangeListener implements IResourceChangeListener {
|
||||||
// Project. Add it to the list of deltas so we can update
|
// Project. Add it to the list of deltas so we can update
|
||||||
// the project file later.
|
// the project file later.
|
||||||
deltaList.add(delta);
|
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
|
// Doesn't really matter since this line can only be reached if
|
||||||
|
@ -111,5 +119,9 @@ public class QtResourceChangeListener implements IResourceChangeListener {
|
||||||
if (!deltaList.isEmpty()) {
|
if (!deltaList.isEmpty()) {
|
||||||
new QtProjectFileUpdateJob(deltaList).schedule();
|
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);
|
String prefix = lastWord(document, offset);
|
||||||
// Save the file
|
// Save the file
|
||||||
IFileEditorInput fileInput = (IFileEditorInput) editor.getEditorInput();
|
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 {
|
try {
|
||||||
String contents = document.get();
|
String contents = document.get();
|
||||||
analyzer.addFile(fileName, contents);
|
Collection<QMLTernCompletion> completions = analyzer.getCompletions(fileName, contents, offset);
|
||||||
Collection<QMLTernCompletion> completions = analyzer.getCompletions(fileName, offset);
|
|
||||||
if (!completions.isEmpty()) {
|
if (!completions.isEmpty()) {
|
||||||
ICompletionProposal[] proposals = new ICompletionProposal[completions.size()];
|
ICompletionProposal[] proposals = new ICompletionProposal[completions.size()];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -64,8 +63,7 @@ public class QMLContentAssistProcessor implements IContentAssistProcessor {
|
||||||
return proposals;
|
return proposals;
|
||||||
}
|
}
|
||||||
} catch (NoSuchMethodException | ScriptException e) {
|
} catch (NoSuchMethodException | ScriptException e) {
|
||||||
// TODO Auto-generated catch block
|
Activator.log(e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
return NO_COMPLETIONS;
|
return NO_COMPLETIONS;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue