Package com.gstaykov.pscoder.editor.completion

Source Code of com.gstaykov.pscoder.editor.completion.CompletionDictionary

/*
* Copyright 2008 Georgi Staykov
*
* This file is part of pscoder.
*
* pscoder is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* pscoder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pscoder.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.gstaykov.pscoder.editor.completion;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposal;

import com.gstaykov.pscoder.Activator;
import com.gstaykov.pscoder.preferences.PreferenceConstants;
import com.gstaykov.pscoder.util.Logger;
import com.gstaykov.pscoder.util.Util;

public class CompletionDictionary {
  private static final String DICT_FILENAME_SUFFIX = "Dictionary.dic";
  private static final String[] GET_ASSEMBLIES_CMD = new String[] {"powershell.exe", "-Command", "\"(Get-PSSnapin) | select AssemblyName | foreach {write-host $_.AssemblyName}\""};
  private static final String CMDLET_FINDER_FILENAME = "CmdletFinder.exe";
  public static final String PS_CMDLET_FILENAME = "CmdletFile";
    public static final String CUSTOM_CMDLET_FILENAME = "CustomCmdletFile";
  private static final String POWERSHELL_FILE_SUFFIX = ".ps1";
  private static final int READ_CHARS = 1024;
  private static HashMap<String, CompletionDictionary> instances = new HashMap<String, CompletionDictionary>();
  private static ResourceChangeListener changeListener = null;
 
  private IProject project = null;
  private String projectName = null;
  private IWorkspace workspace = null;
  private IWorkspaceRoot workspaceRoot = null;
  private Pattern srcFilesP = Pattern.compile("\\.\\s*([A-Za-z0-9\\\\/-]*\\.ps1)"); // FIXME: [IN] Should not match commented imports
  private Pattern functionsP = Pattern.compile("function \\s*([a-zA-Z0-9]*\\(.*\\))");
  private Pattern variablesP = Pattern.compile("\\$(script|global):[a-zA-z0-9_]*");
  private Logger logger = new Logger();
 
  private HashMap<String, FileData> dict = new HashMap<String, FileData>();
 
  private CompletionDictionary(String projName) throws CoreException {
      this.projectName = projName;
    workspace = ResourcesPlugin.getWorkspace();
    workspaceRoot = workspace.getRoot();
    project = workspaceRoot.getProject(projName);
    if (project.exists()) {
      if (!project.isOpen()) project.open(null);
      if (dict.size() == 0) {
          loadDictionary();
      }
    }
  }
 
  private void loadDictionary() throws CoreException {
      File dictFile = new File(projectName + DICT_FILENAME_SUFFIX);
      if (!dictFile.exists()) {
          generateDictionary();
      } else {
          try {
              BufferedReader in = new BufferedReader(new FileReader(dictFile));
              int files = Integer.parseInt(in.readLine());
              for (int i = 0; i < files; i++) {
                  FileData data = new FileData(in.readLine());
                  data.setSourcedFiles(getDictFileNextBlock(in));
                  data.setFunctions(getDictFileNextBlock(in));
                  data.setVariables(getDictFileNextBlock(in));
                  dict.put(data.fileName, data);
              }
          } catch (Exception e) {
              logger.logError("", e);
          }
      }
  }
 
  private String[] getDictFileNextBlock(BufferedReader in) throws IOException {
      int linesCount = Integer.parseInt(in.readLine());
      String[] lines = new String[linesCount];
      for (int i = 0; i < linesCount; i++) lines[i] = in.readLine();
      return lines;
  }
 
  private void generateDictionary() throws CoreException {
    IPath projectRootPath = project.getLocation();
    File projectRoot = projectRootPath.toFile();
    addSourceDirectoryToDict(projectRoot);
   
    IResource[] members = project.members();
    for (IResource member : members) {
        if (member.isLinked()) {
            addSourceDirectoryToDict(member.getLocation().toFile());
        }
    }
   
    addKnownCmdlets();
   
    saveDictionary();
  }
 
  private void addKnownCmdlets() {
      try {
          // First add powershell native cmdlets
          ArrayList<String> assemblies = new ArrayList<String>();
          Process proc = Runtime.getRuntime().exec(GET_ASSEMBLIES_CMD);
          proc.getOutputStream().close();
          BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
          String newLine = null;
          while ((newLine = reader.readLine()) != null) {
              assemblies.add(newLine);
          }
          reader.close();
         
          ArrayList<String> cmdlets = new ArrayList<String>();
          for (String assembly : assemblies) {
              proc = Runtime.getRuntime().exec(new String[] {CMDLET_FINDER_FILENAME, assembly});
              reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
              newLine = null;
              while ((newLine = reader.readLine()) != null) {
                  cmdlets.add(getCmdletString(newLine));
              }
              reader.close();
          }
          FileData data = new FileData(PS_CMDLET_FILENAME);
            data.setFunctions(cmdlets.toArray(new String[cmdlets.size()]));
            dict.put(PS_CMDLET_FILENAME, data);
         
            // Now add custom snapins
            cmdlets.clear();
          String[] customFiles = parsePSSnapinFiles(Activator.getDefault().getPluginPreferences().getString(PreferenceConstants.PS_SNAPIN_FILES));
          for (int i = 0; i < customFiles.length; i++) {
                proc = Runtime.getRuntime().exec(new String[] {CMDLET_FINDER_FILENAME, customFiles[i]});
                reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
                newLine = null;
                while ((newLine = reader.readLine()) != null) {
                    cmdlets.add(getCmdletString(newLine));
                }
                reader.close();
          }
         
            data = new FileData(CUSTOM_CMDLET_FILENAME);
            data.setFunctions(cmdlets.toArray(new String[cmdlets.size()]));
            dict.put(CUSTOM_CMDLET_FILENAME, data);
      } catch (Exception e) {
          logger.logError("An error occured while adding known commandlets", e);
      }
  }
 
  private String getCmdletString(String cmdlet) {
      StringBuffer sb = new StringBuffer();
      StringTokenizer st = new StringTokenizer(cmdlet, ",");
      if (st.hasMoreTokens()) {
          sb.append(st.nextToken());
      }
     
      while (st.hasMoreTokens()) {
          sb.append(" -" + st.nextToken());
      }
     
      return sb.toString();
  }
 
  // FIXME: This does exactly the same job as FileEditor.parseString - make utility method
  private String[] parsePSSnapinFiles(String fileList) {
        StringTokenizer st = new StringTokenizer(fileList, PreferenceConstants.SNAPIN_FILES_SEPARATOR);
        ArrayList<String> elements = new ArrayList<String>();
        while (st.hasMoreTokens()) {
            String token = st.nextToken();
            if (token.length() != 0) {
                elements.add(token);
            }
        }
        return elements.toArray(new String[elements.size()]);
  }
 
  private void addSourceDirectoryToDict(File directory) {
    String[] files = directory.list();
    for (int i = 0; i < files.length; i++) {
      File file = new File(directory.getAbsolutePath() + File.separatorChar + files[i]);
      if (file.isDirectory()) {
        addSourceDirectoryToDict(file);
      }
     
      addFileToDict(file);
    }
  }
 
  private void addFileToDict(File file) {
        if (file.getName().endsWith(POWERSHELL_FILE_SUFFIX)) {
            String fileRelPath = file.getAbsolutePath();
            FileData data = new FileData(fileRelPath);
           
            String text = loadFile(file);
            data.setSourcedFiles(getSourcedFiles(text));
            data.setFunctions(getFunctions(text));
            data.setVariables(getVariables(text));
            dict.put(fileRelPath, data);
        }
  }
 
  private String[] getSourcedFiles(String content) {
    Set<String> sourced = new HashSet<String>();
   
    Matcher matcher = srcFilesP.matcher(content);
    while(matcher.find()) {
        sourced.add(matcher.group(1));
    }
   
    String[] result = new String[sourced.size()];
    return sourced.toArray(result);
  }
 
  private String[] getFunctions(String content) {
        Set<String> sourced = new HashSet<String>();
       
        Matcher matcher = functionsP.matcher(content);
        while(matcher.find()) {
            sourced.add(matcher.group(1));
        }
       
        String[] result = new String[sourced.size()];
        return sourced.toArray(result);
     
  }
 
  private String[] getVariables(String content) {
        Set<String> sourced = new HashSet<String>();
       
        Matcher matcher = variablesP.matcher(content);
        while(matcher.find()) {
            sourced.add(matcher.group(0));
        }
       
        String[] result = new String[sourced.size()];
        return sourced.toArray(result);
  }
 
  private String loadFile(File file) {
    StringBuffer sb = new StringBuffer();
    char[] buffer = new char[READ_CHARS];
   
    try {
      BufferedReader br = new BufferedReader(new FileReader(file));
      int readChar = br.read(buffer);
      while (readChar != -1) {
        sb.append(buffer, 0, readChar);
        readChar = br.read(buffer);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
   
    return sb.toString();
  }
 
  private void saveDictionary() {
      File dictFile = new File(projectName + DICT_FILENAME_SUFFIX);
      try {
          PrintWriter out = new PrintWriter(dictFile);

          Set<String> keys = dict.keySet();
          out.println(keys.size());
         
          for (String key : keys) {
              FileData data = dict.get(key);
              out.println(data.fileName);
             
              // write included files
              String[] incFiles = data.getSourcedFiles();
              out.println(incFiles.length);
              for (int i = 0; i < incFiles.length; i++) {
                  out.println(incFiles[i]);
              }

                // write functions
                String[] functions = data.getFunctions();
                out.println(functions.length);
                for (int i = 0; i < functions.length; i++) {
                    out.println(functions[i]);
                }

                // write variables
                String[] variables = data.getVariables();
                out.println(variables.length);
                for (int i = 0; i < variables.length; i++) {
                    out.println(variables[i]);
                }
          }
         
          out.close();
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
 
  public static CompletionDictionary getInstance(String project) throws CoreException {
      CompletionDictionary result = CompletionDictionary.instances.get(project);
      if (result == null) {
          result = new CompletionDictionary(project);
          CompletionDictionary.instances.put(project, result);
      }
     
      if (changeListener == null) {
          // Install a resource change listener which should update the dictionaries if a workspace resource is changed
          changeListener = new ResourceChangeListener();
          ResourcesPlugin.getWorkspace().addResourceChangeListener(changeListener, IResourceChangeEvent.POST_CHANGE);
      }
     
      return result;
  }
 
  public String getFullFilenameForEnding(String ending) {
    String lowerCasedEnding = Util.convertFileNameToWindowsNotation(ending).toLowerCase();
    String result = ending;

    // FIXME: HACK NOT CORRECT AND THE IMPLEMENTATION IS UGLY
        Set<String> keys = dict.keySet();
        for (String key : keys) {
            if (key.toLowerCase().endsWith(lowerCasedEnding)) {
                result = key;
                break;
            }
        }

        return result;
  }
 
  public HashMap<String, ICompletionProposal> getProposals(String prefix, String document, int offset) {
      HashMap<String, ICompletionProposal> proposals = new HashMap<String, ICompletionProposal>();
     
      // FIXME: HACK NOT CORRECT AND THE IMPLEMENTATION IS UGLY
      Set<String> keys = dict.keySet();
      for (String key : keys) {
          if (key.toLowerCase().endsWith(document.toLowerCase())) {
              document = key;
              break;
          }
      }
        // HACK MIGHT NOT BE CORRECT
     
      FileData data = dict.get(document);
      if (data == null) {
          // No proposals for this document
          return proposals;
      }
     
      String fileNameIncluded = document.substring(document.lastIndexOf('\\') + 1);
      String[] variables = data.getVariables();
      for (int i = 0; i < variables.length; i++) {
          if (variables[i].startsWith(prefix)) {
              String displayString = variables[i] + " - " + fileNameIncluded;
              String stringToAdd = variables[i].substring(prefix.length());
              CompletionProposal prop = new CompletionProposal(stringToAdd, offset, 0, stringToAdd.length(), null, displayString, null, null);
              proposals.put(displayString, prop);
          }
      }
     
       String[] functions = data.getFunctions();
       for (int i = 0; i < functions.length; i++) {
           String funcName = "";
           if (functions[i].indexOf('(') != -1) {
               // Add regular functions
               funcName = functions[i].substring(0, functions[i].indexOf('('));
           } else if (functions[i].indexOf(' ') != -1) {
               // Add cmdlets
               funcName = functions[i].substring(0, functions[i].indexOf(' '));
           } else {
               // Nothing to add
               break;
           }
          
           if (funcName.startsWith(prefix)) {
               String displayString = functions[i] + " - " + fileNameIncluded;
               String stringToAdd = funcName.substring(prefix.length());
               CompletionProposal prop = new CompletionProposal(stringToAdd, offset, 0, stringToAdd.length(), null, displayString, null, null);
               proposals.put(displayString, prop);
           }
       }

       String[] includes = data.getSourcedFiles();
       for (int i = 0; i < includes.length; i++) {
           proposals.putAll(getProposals(prefix, includes[i], offset));
       }
      
       return proposals;
  }
 
  public void loadFileChanges(File file) {
      addFileToDict(file);
     
      // FIXME: For now every time a file is changed save the new dictionary
      saveDictionary();
  }
 
  public void rebuildDictionary() throws CoreException {
      File dictFile = new File(projectName + DICT_FILENAME_SUFFIX);
      if (dictFile.exists()) {
          dictFile.delete();
      }
     
      dict = new HashMap<String, FileData>();
     
        generateDictionary();
  }
 
  public FileData getFileData(String filename) {
      return dict.get(filename);
  }
}
TOP

Related Classes of com.gstaykov.pscoder.editor.completion.CompletionDictionary

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.