Jump to: navigation, search

Difference between revisions of "DLTK IDE Guide:Step 1 Skeleton"

(added missing method getHeaderPatterns() to ExamplePythonContentDescriber)
(Content type for Python files)
Line 56: Line 56:
 
==== Content type for Python files ====
 
==== Content type for Python files ====
  
Content types allow Eclipse to identify that the exact file has a specific content, e.g. python code. The identification can be done by checking a file extension or a file content. Python file extension is *'''.py''' and specified in '''file-extensions''' attribute. Content describer is used to analyze the file content (mostly for files without an extension). For example, it's possible to check that the first file line is a "#\! /usr/local/bin/python".
+
Content types allow Eclipse to identify that the exact file has a specific content, e.g. python code. The identification can be done by checking a file extension or a file content. Python file extension is *'''.py''' and specified in '''file-extensions''' attribute. Content describer is used to analyze the file content (mostly for files without an extension). For example, it's possible to check that the first file line is a "#\!/usr/bin/python".
 
<source lang="xml">
 
<source lang="xml">
 
<extension point="org.eclipse.core.runtime.contentTypes">
 
<extension point="org.eclipse.core.runtime.contentTypes">
Line 76: Line 76:
 
}
 
}
  
/**
+
protected static Pattern[] header_patterns = {
* This method can be extended to use pattern matching for files
+
    Pattern.compile("^#!.*python.*", Pattern.MULTILINE)
        * without extension.
+
        };  
*/
+
public int describe(Reader contents, IContentDescription description)
+
throws IOException {
+
return ScriptContentDescriber.INDETERMINATE;
+
}
+
  
 
protected Pattern[] getHeaderPatterns() {
 
protected Pattern[] getHeaderPatterns() {
return null;
+
return header_patterns;
 
}
 
}
 
}
 
}

Revision as of 02:00, 14 November 2008

Overview

Each Eclipse based application consists of a set of plug-ins. DLTK based Python IDE is not an exception, so the first implementation effort is the plug-in creation procedure. The best practice is to divide a core functionality and a user interface stuff into separate plug-ins but in this tutorial all classes will be combined in one plug-in just for simplicity. All UI functionality will be placed in .ui. packages.

Requirements

  • Eclipse 3.3 or 3.4
  • Latest DLTK Core from 1.0 Integration build
  • Plugins code from cvs or Eclipse submissions at: [[1]]

Plug-in project creation

Create a plug-in using well-known Eclipse Project Wizard: *New->Plug-in Project*. The name of the new plug-in is org.eclipse.dltk.examples.python:

Dltk 003 new project.png

Plug-in project configuration

First of all add DLTK dependencies to the project. Select MANIFEST.MF file from the project and open it with *Plug-in manifest editor*. Then add dependencies on the following plug-ins:

  • org.eclipse.dltk.core
  • org.eclipse.dltk.ui
  • org.eclipse.core.resource
  • org.eclipse.core.ui.editors
  • org.eclipse.ui.ide

Dltk 006 config project.png

Creating IDE based stuff

Project nature

The project nature is contributed using org.eclipse.core.resource.natures extension point. Nature identifier is a project name concatenated with the nature name: org.eclipse.dltk.examples.python.nature:

<extension id="nature" point="org.eclipse.core.resources.natures">
      <runtime>
         <run class="org.eclipse.dltk.examples.internal.python.core.ExamplePythonNature"/>
      </runtime>
   </extension>

ExamplePythonNature is a class that extends ScriptNature class. SriptNature has a stuff for the nature management and sets up the incremental builder for the project.

ExamplePythonNature.java

package org.eclipse.dltk.examples.python;
 
import org.eclipse.dltk.core.ScriptNature;
 
public class ExamplePythonNature extends ScriptNature {
	public static final String PYTHON_NATURE = PythonCorePlugin.PLUGIN_ID
			+ ".nature";
}

Content type for Python files

Content types allow Eclipse to identify that the exact file has a specific content, e.g. python code. The identification can be done by checking a file extension or a file content. Python file extension is *.py and specified in file-extensions attribute. Content describer is used to analyze the file content (mostly for files without an extension). For example, it's possible to check that the first file line is a "#\!/usr/bin/python".

<extension point="org.eclipse.core.runtime.contentTypes">
      <content-type
            base-type="org.eclipse.core.runtime.text"
            describer="org.eclipse.dltk.examples.internal.python.core.ExamplePythonContentDescriber"
            file-extensions="py"
            id="org.eclipse.dltk.examples.python.content-type"
            name="Example Python content type"
            priority="high">
      </content-type>
   </extension>

ExamplePythonContentDescriber.java

public class ExamplePythonContentDescriber extends ScriptContentDescriber {
	public ExamplePythonContentDescriber() {
	}
 
	protected static Pattern[] header_patterns = {
	    Pattern.compile("^#!.*python.*", Pattern.MULTILINE)
        }; 
 
	protected Pattern[] getHeaderPatterns() {
		return header_patterns;
	}
}

Eclipse has a powerful file associations based on contributed content types. These associations can be easily configured from preferences: *General->Content Types*. New file extensions can be added for already defined content types: Dltk 009 content types.png

DLTK Language Toolkit

Language Toolkit class is the core part of DLTK based script IDE. It's the entry point for access all language specific features. Language Toolkit is added to DLTK using org.eclipse.dltk.language extension point. For python language toolkit we use the created nature identifier and a class which implements IDLTKLanguageToolkit interface:

<extension point="org.eclipse.dltk.core.language">
      <language
            class="org.eclipse.dltk.examples.internal.python.core.ExamplePythonLanguageToolkit"
            nature="org.eclipse.dltk.examples.python.nature"
            priority="0">
      </language>
   </extension>

AbstractLanguageToolkit class is a good base which implements IDLTKLanguageToolkit with additional useful methods:

ExamplePythonLanguageToolkit.java

public class ExamplePythonLanguageToolkit extends AbstractLanguageToolkit {
	private static ExamplePythonLanguageToolkit toolkit;
 
	public static IDLTKLanguageToolkit getDefault() {
		if (toolkit == null) {
			toolkit = new ExamplePythonLanguageToolkit();
		}
		return toolkit;
	}
 
	public String getLanguageName() {
		return "Python";
	}
 
	public String getNatureId() {
		return ExamplePythonNature.PYTHON_NATURE;
	}
 
	public String getLanguageContentType() {
		return "org.eclipse.dltk.examples.python.content-type";
	}
}

DLTK UI-Language Toolkit

Most of UI language-specific things are provided by UI-language toolkit:

  • Label providers
  • Editor partitioning information
  • Source viewer configuration
  • Default editor information
  • Default preference page information

UI-Language toolkit is contributed using org.eclipse.dltk.ui.language extension point:

<extension point="org.eclipse.dltk.ui.language">
      <language
            class="org.eclipse.dltk.examples.internal.python.ui.ExamplePythonUILanguageToolkit"
            nature="org.eclipse.dltk.examples.python.nature"
            priority="0">
      </language>
   </extension>

AbstractDLTKUILanguageToolkit is the default implementation of IDLTKUILanguageToolkit interface and it plays the same role a AbstractDLTKLanguageToolkit for IDLTKLanguageToolkit.

ExamplePythonUILanguageToolkit.java

public class ExamplePythonUILanguageToolkit extends AbstractDLTKUILanguageToolkit {
	protected AbstractUIPlugin getUIPLugin() {
		return PythonCorePlugin.getDefault();
	}
 
	public IDLTKLanguageToolkit getCoreToolkit() {
		return ExamplePythonLanguageToolkit.getDefault();
	}
}

Structure parser (simple implementation)

DLTK builds the following structure for each project and source module:

Project
 ├─── Project Fragment
 │     ├─── Script Folder
 │     │     ├─── Source Module
 │     │     │      ├─── Field
 │     │     │      ├─── Method
 │     │     │      ├─── Class
 │     │     │      │     ├─── Field
 │     │     │      │     ├─── Method
 │     │     │      │     ├─── Class
...   ...   ...    ...   ...   ...
 │     │     │      │     │

For Projects, Project Fragments and Script Folders the model is built automatically. Source element parsers are used to create a model of Source Module (with Fields, Methods and Classes).

Create a simple source element parser to demonstrate the power of DLTK. This parser will "convert" raw python script files to the set of Java objects (Classes, Fields, Methods) representing a model of code. Then this model will be used for different purposes, e.g. for Python syntax checking, for visual representation (Outline View, Script Explorer) etc. ExamplePythonSourceElementParser.java

public class ExamplePythonSourceElementParser extends AbstractSourceElementParser {
	public void parseSourceModule(char[] contents,
			ISourceModuleInfo astCache, char[] filename) {
		ISourceElementRequestor requestor = getRequestor();
 
		requestor.enterModule();
		TypeInfo info = new TypeInfo();
		info.name = "Example type";
		requestor.enterType(info);
		requestor.exitType(0);
		requestor.exitModule(0);
	}
}

Described source element parser creates the same code model for all python files without paying attention to their content. The model is very simple: one source module with one type definition.

Project wizard

It's necessary to create a Python project wizard to test python language implementation. To create a project wizard it's required to contribute to the org.eclipse.ui.newWizard extension point:

<extension point="org.eclipse.ui.newWizards">
      <wizard
            category="org.eclipse.dltk.examples.python.category"
            class="org.eclipse.dltk.ui.wizards.GenericDLTKProjectWizard:org.eclipse.dltk.examples.python.nature"
            icon="icons/etool16/newprj_wiz.gif"
            id="org.eclipse.dltk.examples.python.wizard1"
            name="Example Python ProjectWizard"
            project="true">
      </wizard>
      <category
            id="org.eclipse.dltk.examples.python.category"
            name="DLTK Examples">
      </category>
   </extension>

This extension point uses GenericDLTKPRojectWizard class with the python nature identifier as a parameter.

First Python project

Python project wizard has been already added. Now it can be used for the first python Project creation:

File:Dltk 013 new example python project.png Dltk 013 new example python project first page.png

New python files can be added to the recently created project using a NewFile dialog. Script Explorer will display the project structure and the model of python code. Each file in Script Explorer can be expanded to see the code model:

Dltk 014 script explorer pc.png => Dltk 014 script explorer files.png

Python interpreter

To execute python files the IDE should have knowledge about python interpreters. DLTK has a support of generic interpreter management that can be extended in a flexible way by the IDE. Support of interpreter also gives an access to all its builtin libraries.

It's required to add the following plug-ins to the dependency list in the plug-in manifest:

  • org.eclipse.dltk.launching
  • org.eclipse.dltk.debug
  • org.eclipse.dltk.debug.ui
  • org.eclipse.debug.core
  • org.eclipse.debug.ui.

Each script language need to contribute at least one *Interpreter Install Type*. It's possible to create several interpreter install types for different python interpreters: common python, cpython and jython. There can be several installations or *Install Locations* of the same interpreter install type. For example it's possible to have python 2.4 and python 2.5 installed on the same machine.

<extension point="org.eclipse.dltk.launching.interpreterInstallTypes">
      <interpreterInstallType
            class="org.eclipse.dltk.examples.internal.python.interpreter.ExamplePythonInstallType"
            id="org.eclipse.dltk.internal.debug.ui.launcher.ExamplePythonInstallType">
      </interpreterInstallType>
   </extension>

ExamplePythonInstallType.java

public class ExamplePythonInstallType extends AbstractInterpreterInstallType {
	private static final String[] INTERPRETER_NAMES = { "python", "pythonw" };
 
	public String getNatureId() {
		return ExamplePythonNature.PYTHON_NATURE;
	}
 
	public String getName() {
		return "Example Python";
	}
 
	protected String getPluginId() {
		return PythonCorePlugin.PLUGIN_ID;
	}
 
	protected String[] getPossibleInterpreterNames() {
		return INTERPRETER_NAMES;
	}
 
	protected IInterpreterInstall doCreateInterpreterInstall(String id) {
		return new ExamplePythonInstall(this, id);
	}
 
	protected File createPathFile() throws IOException {
		return storeToMetadata(PythonCorePlugin.getDefault(), "path.py", "scripts/path.py");
	}
 
	protected ILog getLog() {
		return PythonCorePlugin.getDefault().getLog();
	}
}

AbstractInterpreterInstallType class is a base for custom interpreter install type. This class contains the interpreter management logic and gives an easy access to the interpreter library locations. Each interpreter install has a set of configured library location paths. Interpreter install type uses a native python script to obtain the list of libraries: path.py

import sys
    print " ".join( sys.path )

This script returns a set of library paths separated by a space.

ExamplePythonInstallType class provides the possible interpreter names: python, pythonw. ExamplePythonInstall.java

public class ExamplePythonInstall extends AbstractInterpreterInstall {
	public ExamplePythonInstall(IInterpreterInstallType type, String id) {
		super(type, id);
	}
 
	public String getNatureId() {
		return ExamplePythonNature.PYTHON_NATURE;
	}
}
 
The interpreter preference page support can be added using the '''org.eclipse.ui.preferencePages''' extension point:
<source lang="xml">
<extension
         point="org.eclipse.ui.preferencePages">
      <page class="org.eclipse.dltk.examples.internal.python.interpreter.ui.ExamplePythonInterpreterPreferencePage"
            id="org.eclipse.dltk.python.examples.preferences.interpreters"
            name="Example Python Interpreters"/>
   </extension>

The following set of classes is used for the preference dialog support. All DLTK preferences are based on configuration blocks (like JDT).

</source> ExamplePythonInterpreterPreferencePage.java

public class ExamplePythonInterpreterPreferencePage extends
		ScriptInterpreterPreferencePage {
	public static final String PAGE_ID = "org.eclipse.dltk.python.examples.preferences.interpreters";
 
	public InterpretersBlock createInterpretersBlock() {
		return new ExamplePythonInterpretersBlock();
	}
}

The configuration block for the interpreter:

ExamplePythonInterpretersBlock.java

public class ExamplePythonInterpretersBlock extends InterpretersBlock {
	protected AddScriptInterpreterDialog createInterpreterDialog(
			IInterpreterInstall standin) {
		ExampleAddPythonInterpreterDialog dialog = new ExampleAddPythonInterpreterDialog(
				this, getShell(), ScriptRuntime
						.getInterpreterInstallTypes(getCurrentNature()),
				standin);
		return dialog;
	}
 
	protected String getCurrentNature() {
		return ExamplePythonNature.PYTHON_NATURE;
	}
}

ExampleAddPythonInterpreterDialog.java

public class ExampleAddPythonInterpreterDialog extends AddScriptInterpreterDialog {
	public ExampleAddPythonInterpreterDialog(IAddInterpreterDialogRequestor requestor,
             Shell shell, IInterpreterInstallType[] interpreterInstallTypes, IInterpreterInstall editedInterpreter) {
		super(requestor, shell, interpreterInstallTypes, editedInterpreter);
	}
 
	protected AbstractInterpreterLibraryBlock createLibraryBlock(AddScriptInterpreterDialog dialog) {
		return new ExamplePythonInterpreterLibraryBlock(dialog);
	}
}

ExamplePythonInterpreterLibraryBlock.java

public class ExamplePythonInterpreterLibraryBlock extends AbstractInterpreterLibraryBlock {
      public ExamplePythonInterpreterLibraryBlock(AddScriptInterpreterDialog d) {
             super(d);
      }
 
      protected IBaseLabelProvider getLabelProvider() {
 		return new LibraryLabelProvider();
      }
 
      protected IDialogSettings getDialogSettions() {
                return PythonCorePlugin.getDefault().getDialogSettings();
      }
}

All tedious stuff was implemented and now the interpreter can be seen in action:

Dltk 015 interpreter config 0.png

The "Search" button makes all magic for interpreter searching. All folders from the system PATH variable are scanned (with the deep of two). Interpreter can be added manually by clicking on "Add" button.

Dltk 015 interpreter config 1.png

The selected interpreter is visible from Script Explorer as shown below: Dltk 015 interpreter config 2.png

Getting the code

The tutorial source code is available from Eclipse CVS:

  • HOST: dev.eclipse.org
  • CVSROOT: /cvsroot/technology/
  • MODULE: org.eclipse.dltk/examples/eclipsecon08/org.eclipse.dltk.examples.python
  • CONNECTION TYPE: pserver
  • USERNAME: anonymous