1
0
Fork 0
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:
Matthew Bastien 2015-12-04 17:08:10 -05:00 committed by Gerrit Code Review @ Eclipse.org
parent 3a2cb0431e
commit 42e3859b68
8 changed files with 186 additions and 18 deletions

View file

@ -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<>();

View file

@ -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")));
}

View file

@ -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);
};
}

View file

@ -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) !== "/") {

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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();
}
}
}

View file

@ -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;
}