/*
* Copyright 2011 Jon S Akhtar (Sylvanaar)
*
* Licensed 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 com.sylvanaar.idea.Lua.lang.documentor;
import com.intellij.lang.documentation.DocumentationProvider;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.vfs.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiNamedElement;
import com.intellij.util.PathUtil;
import com.sylvanaar.idea.Lua.lang.psi.LuaPsiFile;
import com.sylvanaar.idea.Lua.util.UrlUtil;
import org.jetbrains.annotations.Nullable;
import se.krka.kahlua.converter.KahluaConverterManager;
import se.krka.kahlua.integration.LuaCaller;
import se.krka.kahlua.integration.LuaReturn;
import se.krka.kahlua.integration.annotations.LuaMethod;
import se.krka.kahlua.integration.expose.LuaJavaClassExposer;
import se.krka.kahlua.j2se.J2SEPlatform;
import se.krka.kahlua.luaj.compiler.LuaCompiler;
import se.krka.kahlua.vm.KahluaTable;
import se.krka.kahlua.vm.KahluaThread;
import se.krka.kahlua.vm.LuaClosure;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by IntelliJ IDEA.
* User: Jon S Akhtar
* Date: 2/20/11
* Time: 3:06 PM
*/
public class KahluaPluginDocumentationProvider implements DocumentationProvider {
private static final KahluaConverterManager converterManager = new KahluaConverterManager();
private static final J2SEPlatform platform = new J2SEPlatform();
private static final LuaCaller caller = new LuaCaller(converterManager);
private final Object VMLock = new Object();
private static final Logger log = Logger.getInstance("Lua.documenter.KahluaPluginDocumentationProvider");
private static final String DOC_FILE_SUFFIX = ".doclua";
private static final Map<VirtualFile, ScriptEnvironment> scriptEnvironmentMap =
new HashMap<VirtualFile, ScriptEnvironment>();
private class ScriptEnvironment {
KahluaTable env = platform.newEnvironment();
final KahluaThread thread = new KahluaThread(platform, env);
final LuaJavaClassExposer exposer = new LuaJavaClassExposer(converterManager, platform, env);
}
@LuaMethod(name="log", global = true)
public void luaLog(String msg) {
log.info(msg);
}
@LuaMethod(name="disableCache", global = true)
public void clearCaches() {
scriptEnvironmentMap.clear();
}
@LuaMethod(name="fetchURL", global = true)
public String fetchURL(String url) {
UrlUtil.UrlFetcher fetcher = new UrlUtil.UrlFetcher(url);
fetcher.run();
return fetcher.getData();
}
@LuaMethod(name="getBaseJarUrl", global = true)
public String getBaseJarUrl() {
String url = VfsUtil.pathToUrl(PathUtil.getJarPathForClass(LuaPsiFile.class));
VirtualFile sdkFile = VirtualFileManager.getInstance().findFileByUrl(url);
if (sdkFile != null) {
VirtualFile jarFile = JarFileSystem.getInstance().getJarRootForLocalFile(sdkFile);
if (jarFile != null) {
return jarFile.getUrl();
} else {
return sdkFile.getUrl();
}
}
return null;
}
@Override
public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) {
if (element instanceof PsiNamedElement)
return runLuaQuickNavigateDocGenerator(getVirtualFileForElement(element), ((PsiNamedElement) element).getName());
return null;
}
@Override
public List<String> getUrlFor(PsiElement element, PsiElement originalElement) {
String s = runLuaDocumentationUrlGenerator(getVirtualFileForElement(element), element.getText());
if (s == null) return null;
List<String> rc = new ArrayList<String>();
rc.add(s);
return rc;
}
@Override
public String generateDoc(PsiElement element, PsiElement originalElement) {
return runLuaDocumentationGenerator(getVirtualFileForElement(element), element.getText());
}
@Override
public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) {
return null;
}
@Override
public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) {
return null;
}
@Nullable
private VirtualFile getVirtualFileForElement(PsiElement e) {
PsiElement r = e;
// if (e instanceof LuaDeclarationExpression) {
// r = e;
// } else {
// if (e instanceof LuaFunctionCallExpression)
// e = ((LuaFunctionCallExpression) e).getFunctionNameElement();
//
// if (e instanceof LuaFieldIdentifier) {
// e = ((LuaFieldIdentifier) e).getEnclosingIdentifier();
//
// assert e instanceof LuaCompoundIdentifier;
// }
//
// while (e instanceof LuaCompoundIdentifier) {
// e = e.getParent();
// }
//
// if (! (e instanceof LuaReferenceElement))
// e = e.getParent();
//
//
// if (e instanceof LuaReferenceElementImpl) {
// r = ((LuaReferenceElementImpl) e).getResolvedElement();
// }
// }
if (r != null) {
VirtualFile vf = r.getContainingFile().getVirtualFile();
String docFileName = null;
if (vf != null) {
docFileName = vf.getNameWithoutExtension() + DOC_FILE_SUFFIX;
return vf.getParent().findChild(docFileName);
}
}
return null;
}
private ScriptEnvironment getScriptEnvironmentForFile(VirtualFile vf) throws IOException {
synchronized (VMLock) {
if (scriptEnvironmentMap.containsKey(vf))
return scriptEnvironmentMap.get(vf);
ScriptEnvironment scriptEnvironment = new ScriptEnvironment();
scriptEnvironment.exposer.exposeGlobalFunctions(this);
// Cache the environment
scriptEnvironmentMap.put(vf, scriptEnvironment);
// Run the initial script
LuaClosure closure = LuaCompiler.loadis(vf.getInputStream(), vf.getName(), scriptEnvironment.env);
LuaReturn rc = caller.protectedCall(scriptEnvironment.thread, closure);
if (!rc.isSuccess())
log.info("Error during initial lua call: " + rc.getErrorString() + "\r\n\r\n" + rc.getLuaStackTrace());
return scriptEnvironment;
}
}
@Nullable
private String runLuaQuickNavigateDocGenerator(@Nullable VirtualFile luaFile, String nameToDocument) {
return runLua("getQuickNavigateDocumentation", luaFile, nameToDocument);
}
@Nullable
private String runLuaDocumentationUrlGenerator(@Nullable VirtualFile luaFile, String nameToDocument) {
return runLua("getDocumentationUrl", luaFile, nameToDocument);
}
@Nullable
private String runLuaDocumentationGenerator(@Nullable VirtualFile luaFile, String nameToDocument) {
return runLua("getDocumentation", luaFile, nameToDocument);
}
@Nullable
private String runLua(String function, @Nullable VirtualFile luaFile, String nameToDocument) {
if (luaFile == null) return null;
String docLuaFileUrl = luaFile.getParent().getUrl();
synchronized (VMLock) {
try {
ScriptEnvironment scriptEnvironment = getScriptEnvironmentForFile(luaFile);
if (scriptEnvironment == null) return null;
LuaClosure closure = LuaCompiler.loadstring(new StringBuilder().append("return ")
.append(function)
.append("('")
.append(nameToDocument)
.append("', '")
.append(docLuaFileUrl)
.append("')")
.toString(), "", scriptEnvironment.env
);
LuaReturn rc = caller.protectedCall(scriptEnvironment.thread, closure);
if (!rc.isSuccess())
log.info("Error during lua call: " + rc.getErrorString() + "\r\n\r\n" + rc.getLuaStackTrace());
if (!rc.isEmpty()) {
String unencoded = (String) rc.getFirst();
byte[] bytes = unencoded.getBytes();
return new String(bytes, CharsetToolkit.UTF8);
}
} catch (IOException e) {
log.info("Error in lua documenter", e);
}
}
return null;
}
}