/**
* (c) 2011, Alejandro Serrano
* (c) 2012 by JP Moresmau
* Released under the terms of the EPL.
*/
package net.sf.eclipsefp.haskell.ui.internal.editors.haskell.imports;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import net.sf.eclipsefp.haskell.browser.items.Documented;
import net.sf.eclipsefp.haskell.browser.util.ImageCache;
import net.sf.eclipsefp.haskell.buildwrapper.BWFacade;
import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin;
import net.sf.eclipsefp.haskell.buildwrapper.types.ImportDef;
import net.sf.eclipsefp.haskell.buildwrapper.types.NameDef;
import net.sf.eclipsefp.haskell.buildwrapper.types.OutlineResult;
import net.sf.eclipsefp.haskell.core.util.ResourceUtil;
import net.sf.eclipsefp.haskell.ui.HaskellUIPlugin;
import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.HaskellEditor;
import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.imports.AnImport.FileDocumented;
import net.sf.eclipsefp.haskell.ui.internal.resolve.DiscreteCompletionProposal;
import net.sf.eclipsefp.haskell.util.PlatformUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
/**
* Manages information and changed in the imports section
* of a Haskell source file. All imports must be in only
* one line to be recognized by this parser.
* @author Alejandro Serrano
* @author JP Moresmau
*/
public class ImportsManager {
private final IFile file;
private final IDocument doc;
private ArrayList<AnImport> imports=null;
private Map<String, Documented> decls=null;
private Map<String, Imported> importedDecls=null;
/**
* the names from GHC names in scope
*/
private Collection<NameDef> names=null;
public ImportsManager(final IFile file, final IDocument doc) {
this.file = file;
this.doc = doc;
}
public ImportsManager(final IFile file, final IDocument doc,final Collection<NameDef> names) {
this.file = file;
this.doc = doc;
this.names=names;
}
public void reset(){
imports=null;
decls=null;
importedDecls=null;
}
public ArrayList<AnImport> parseImports() {
if(imports==null){
ArrayList<AnImport> myImports = new ArrayList<>();
OutlineResult or=null;
if (doc!=null){
HaskellEditor editor=HaskellUIPlugin.getHaskellEditor( doc );
if (editor!=null){
or=editor.getLastOutlineResult();
}
}
if (or==null){
BWFacade f=BuildWrapperPlugin.getFacade( file.getProject() );
if (f!=null){
or=f.outline( file ,doc);
}
}
if (or!=null){
for (ImportDef id:or.getImportDefs()){
AnImport imp = new AnImport( id,false );
myImports.add(imp);
}
}
imports=myImports;
}
return imports;
}
public Map<String, Documented> getDeclarations() {
if(decls==null){
Map<String, Imported> si=getImportedDeclarations();
Map<String, Documented> myDecls = new HashMap<>(si.size());
for (String i:si.keySet()) {
myDecls.put(i,si.get( i ).getDocumented().getDocumented() );
}
decls=myDecls;
}
Map<String, Documented> ret=decls;
if (names!=null){
// add the names dynamically
ret=new HashMap<>(decls);
for(NameDef n:names){
if (!ret.containsKey( n.getName() )){
ret.put( n.getName(), AnImport.nameToBrowser( n ) );
}
}
}
return ret;
}
public Map<String, Imported> getImportedDeclarations() {
if(importedDecls==null){
ArrayList<AnImport> imports = parseImports();
// Add Prelude import
boolean hasPrelude = false;
Map<String, Imported> myImportedDecls = new HashMap<>();
// Add me
String meName = ResourceUtil.getModuleName( file );
getImportDeclarations(myImportedDecls, AnImport.createMe( meName ) );
for (AnImport i : imports) {
getImportDeclarations(myImportedDecls,i);
if (i.getImportDef().getModule().equals( "Prelude" )) {
hasPrelude = true;
}
}
if (!hasPrelude) {
getImportDeclarations(myImportedDecls, new AnImport(new ImportDef("Prelude", null, false, false, null ),false ));
}
importedDecls=myImportedDecls;
}
return importedDecls;
}
private void getImportDeclarations(final Map<String, Imported> r,final AnImport i){
Map<String, FileDocumented> ir=i.getDeclarations( file.getProject(), file, doc );
for (String s:ir.keySet()){
r.put(s,new Imported( ir.get( s ), i ) );
}
}
public ICompletionProposal addImport( final String name, final String place, final String qualified, final String label ) {
AnImport lastImport = null;
for (AnImport imp : parseImports()) {
lastImport = imp;
String qname = imp.getImportDef().getAlias();
if (imp.getImportDef().getModule().equals( place ) && ( ((qname == null || qname.length()==0) && qualified == null) || (qname != null && qname.equals( qualified )) ) ) {
// Change in this import
if (imp.getImportDef().getChildren()==null) {
// We didn't need to add an import in first place
return null;
}
if (name==null){
return imp.removeAll( doc, label );
}
if (imp.getImportDef().isHiding()) {
return imp.removeItem( doc, name, label );
}
return imp.addItem( doc, name, label );
}
}
// If we finished here, that means that we need to add a new import
try {
// 1. Get line of the last element and find offset
int line = doc.getLineOfOffset( doc.get().indexOf( "where" ) ) + 1;
if (lastImport != null) {
line=lastImport.getImportDef().getLocation().getEndLine()-1;
//line = doc.getLineOfOffset( lastImport.getLocation().getOffset() );
}
int offsetToPut = doc.getLineOffset( line ) + doc.getLineLength( line );
// 2. Create contents
String contents;
if (name!=null){
if (qualified != null) {
contents = "import qualified " + place + " as " + qualified + " (" + name + ")";
} else {
contents = "import " + place + " (" + name + ")";
}
} else {
if (qualified != null) {
contents = "import qualified " + place + " as " + qualified;
} else {
contents = "import " + place;
}
}
// 3. Create the proposal
return new DiscreteCompletionProposal( contents + PlatformUtil.NL, offsetToPut, 0, ImageCache.MODULE, label, null, "" );
} catch (Exception e) {
HaskellUIPlugin.log( e );
return null;
}
}
// public CompletionProposal removeItemInImport(final String name, final int line, final String label) {
// try {
// for (AnImport imp : parseImports()) {
// int importLine =imp.getImportDef().getLocation().getStartLine();
// //doc.getLineOfOffset( imp.getLocation().getOffset() );
// if (importLine-1 == line) {
// return imp.removeItem( doc, name, label );
// }
// }
// } catch (Exception e) {
// HaskellUIPlugin.log( e );
// }
// return null;
// }
public ICompletionProposal removeItemInImport(final Collection<String> names, final int line, final String label) {
try {
for (AnImport imp : parseImports()) {
int importLine =imp.getImportDef().getLocation().getStartLine();
//doc.getLineOfOffset( imp.getLocation().getOffset() );
if (importLine-1 == line) {
return imp.removeItem( doc, names, label );
}
}
} catch (Exception e) {
HaskellUIPlugin.log( e );
}
return null;
}
public ICompletionProposal replaceItemInImport(final String name,final String newName, final int line, final String label) {
try {
for (AnImport imp : parseImports()) {
int importLine =imp.getImportDef().getLocation().getStartLine();
//doc.getLineOfOffset( imp.getLocation().getOffset() );
if (importLine-1 == line) {
return imp.replaceItem( doc, name,newName, label );
}
}
} catch (Exception e) {
HaskellUIPlugin.log( e );
}
return null;
}
public static class Imported {
private final FileDocumented documented;
private final AnImport animport;
public Imported( final FileDocumented documented, final AnImport animport ) {
super();
this.documented = documented;
this.animport = animport;
}
public FileDocumented getDocumented() {
return documented;
}
public AnImport getAnimport() {
return animport;
}
}
}