Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Orion/How Tos/Code Edit

< Orion‎ | How Tos
Revision as of 09:29, 4 May 2016 by Libingw.ca.ibm.com (Talk | contribs) (How to listen the editor's mode changes after its creation)

Contents

Code Edit Widget With Language Tooling

To make it easier to consume the Orion editor with the complete language tooling, a new widget called "Code Edit" has been introduced. The widget consists of the core editor and all Web development language tools, including the Tern analysis engine and Esprima parser. The widget is delivered as a zip file containing all the structural folders and files, making the core and the plugins all work together. You can easily embed this widget directly into any client side Web application. The language tooling plugins are only loaded when needed, making it easier to embed a powerful code editor without hurting page load time. The widget core is available both unminified, for debugging purpose and minified, for better loading time. The plugins are only available in minified version.

Getting the build

  • Users can grab the most recent "Code Edit" widget from the nightly builds or they can use the latest release available on the Orion download page.
  • From the Orion build page, users can click on a nightly build or a release and they will be presented with a page that now includes the code edit widget: the built-codeEdit.zip file contains all the files for you to embed the widget.

Using the build and the quick start demo

You can simply download the zip file and unzip it into a folder on your web server. Below is a complete run-able html file that you can host in your web server as a demo on how to consume the widget. Assume that you have created a folder named editorBuild under where your html file lives. Follow the 3 steps below and you can run the demo in just a few minutes. The same demo is also running here, where you can see it right away.

  • Download the code edit build and unzip it under the editorBuild folder.
  • Create a demo.html and paste the example contents into the demo.html.
  • Run the demo.html.
<!doctype html>
<html>
    <head>
		<meta name="copyright" content="Copyright (c) IBM Corporation and others 2010, 2014." >
		<meta http-equiv="Content-Language" content="en-us">
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<title>Pluggable Editor Demo</title>
		<style type="text/css">
			.demoTitle{
				border: none;
				vertical-align: middle;
				overflow: hidden;
				text-align: left;
				margin-left: 15%;
				margin-right: 15%;
				padding-bottom: 5px;
				position: relative;
			}
			.demoBody{
				border: 1px solid;
				vertical-align: middle;
				border-color: blue;
				overflow: hidden;
				text-align: left;
				margin-left: 15%;
				margin-right: 15%;
				margin-bottom: 15px;
				padding-bottom: 5px;
				position: relative;
				height: 450px;
			}
		</style>
	    <link rel="stylesheet" type="text/css" href="editorBuild/code_edit/built-codeEdit.css"/>
		<script src="editorBuild/code_edit/built-codeEdit.js"></script>
		<script>
			/*globals orion */
			window.onload=function(){
				var codeEdit = new orion.codeEdit();
				var contents = 'var foo = "bar";\n' +
									 "var bar = foo;\n" + 
									 "/*\n" + 
									 " * test demo\n" + 
									 "*/\n" + 
									 "function test(){\n" + 
									 "	var foo1 = bar.lastIndexOf(char, from);\n" + 
									 "}\n" + 
									"//Keep editting in this demo and try the content assit, probem validations and hover service!\n" +
									 "var foo2 = foo."; 
				codeEdit.create({parent: "embeddedEditor", contentType: "application/javascript", contents: contents}).then(function(editorViewer) {
					document.getElementById("progressMessageDiv").textContent = "Plugins loaded!";
					//You can call APIs from editorViewer.editor  for further actions.
				});
			};
		</script>
    </head>
	<body id="orion-browser" spellcheck="false" class="orionPage">
		<div class="demoTitle">
			<div>This is a demo for the <b>Orion Code Edit</b> widget. This demo consumes the <b>build version</b> of the widget.</div> 
			<div> Keep editing in this demo and try:</div>
			<div> 1.content assist. E.g., put cursor after "foo." at the last line and press CTRL+space.</div>
			<div> 2.probem validations. E.g., modify something and you will see new validation markers coming up, if any</div>
			<div> 3.hover service. Hover on any error markers or inside the eidtor.</div>
			<div> 4.syntax highlighting</div>
			<div> 5.Quick fix. Hover on a problem inside the eidtor, not on the ruler, e.g., (char, from) in this demo. Click on the quick fix and see.</div>
			<div> 6.Find declaration. Select a variable and press f3.</div>
			<div> 7.new tooling features coming while Orion is being improved...</div>
		</div>
		<div class="demoTitle">
			<span id = "progressMessageDiv" style="color: green">Loading language tooling plugins...</span>
		</div>
		</div>
		<div class="demoBody" id="embeddedEditor">
		</div>
	</body>
</html>

Your demo page should look like this, if you use CTRL+space as content assist after the page is loaded.

CodeEditDemo.png

Consuming the widget in different ways

Closer look at the build

If you unzip the build, the file structure looks like below:

WidgetCore.png

There are several folders in the build, you should copy them all to your web server but only reference the files under the code_edit folder. All other files from other folders will be loaded dynamically as plugins depending on the file MIME type. The built-codeEdit.css file provides all the default css classes used in the widget. The built-codeEdit.min.js file provides the widget core code in a compact size while the built-codeEdit.js file provides the readable code for debugging purpose. Both of the .js files can be consumed by either loading the widget directly or using the RequireJS module loader.

Loading the widget directly

Assuming that you've unzipped the build into a folder called editorBuild, the following code in an html file shows you how to load the widget.

<link rel="stylesheet" type="text/css" href="editorBuild/code_edit/built-codeEdit.css"/>
<script src="editorBuild/code_edit/built-codeEdit.min.js"></script>
<script>
    window.onload=function(){
        var codeEdit = new orion.codeEdit();
        //Use codeEdit from here
    };
</script>

Note that orion.codeEdit is the only global function that the built-codeEdit.min.js exports after it is loaded. You need to call the create API to create an editor with your text contents. Details is addressed in a later section.

using the RequireJS module loader

Warning2.png
The information on this section applies only to the code edit widget build after the 10.0 stream stable build S20150629-1217

Assuming that you've unzipped the build into a folder called editorBuild and you want to use the RequireJS module loader, the following code in an html file shows you how to load the widget. Note that the AMD build exposes a module called orion/codeEdit as the widget root. You can also load other Orion modules that are built into the widget, such as orion/Deferred, etc.

<link rel="stylesheet" type="text/css" href="editorBuild/code_edit/built-codeEdit.css"/>
<script>
require.config({
    bundles: {
        "editorBuild/code_edit/built-codeEdit-amd": ["orion/codeEdit", "orion/Deferred"]
    }	    
});
require(["orion/codeEdit", "orion/Deferred"], function(mCodeEdit, Deferred) {
	var codeEdit = new mCodeEdit();
	var contents = 'var foo = "bar";'; 
	codeEdit.create({parent: "embeddedEditor"/*editor parent node id*/}).then(function(editorViewer) {
		editorViewer.setContents(contents, "application/javascript");
	});
});
</script>

Other than using require.config as above, you can also use another flavor of loading as below:

<link rel="stylesheet" type="text/css" href="editorBuild/code_edit/built-codeEdit.css"/>
<script>
require(["editorBuild/code_edit/built-codeEdit-amd"], function() {
    require(["orion/codeEdit", "orion/Deferred"], function(mCodeEdit, Deferred) {
	var codeEdit = new mCodeEdit();
	var contents = 'var foo = "bar";'; 
	codeEdit.create({parent: "embeddedEditor"/*editor parent node id*/}).then(function(editorViewer) {
		editorViewer.setContents(contents, "application/javascript");
	});
    });
});
</script>

Note that the way how the widget is loaded is slightly different but either way you end up with a widget instance called codeEdit. Lets talk about how to use the codeEdit instance in the next section.

using the codeEdit.create function

As described in the previous section, now you are holding an instance of the code edit widget called codeEdit. Every time you call codeEdit.create function, it creates an Orion editor instance and shows your editor text right away. There are two ways to call the codeEdit.create function.

A simple way to call codeEdit.create

If you want to create your editor and leave it there by using all the default settings, refer to the following code.

codeEdit.create({parent: "myEditorDivId", contentType: "application/javascript", contents: "var foo;"});

An advanced way to call codeEdit.create

If you want to create your editor and reuse the editor instance for customization and even for different contents by the same editor instance, refer to the following code.

codeEdit.create({parent: "myEditorDivId"}).then(function(editorViewer) {
    editorViewer.setContents("var foo;", "application/javascript");
    //editorViewer.editor.textView.setText("var bar;");
});

Setting your editor to readonly after the creation

Although there are other ways to create a readonly editor but the simplest way is to set the editorViewer.readonly property to true.

codeEdit.create({parent: "myEditorDivId"}).then(function(editorViewer) {
    editorViewer.setContents("var foo;", "application/javascript");
    editorViewer.readonly = true;
});

Parameters used in the codeEdit.create call

The codeEdit.create function call returns a javascript promise object that you can reuse. Note that either you use the simple or advanced way to create your editor, there are 3 parameters required. When you use the simple way, you just pass an object with {parent, contentType, contents} and you do not care the promise. When you use the advanced way, you only pass {parent} but when you handle the promise object, which is the editorViewer object, you will pass the contents and content type to the editorViewer.setContents. Note that is you hold editorViewer.setContents instance somewhere in your code, you can just reuse the same instance for different contents. This is useful when you want to keep an editor open but keep changing the contents for the time being.

parent parameter

This can be either a DIV id or DIV node that holds your editor.

contents parameter

This is a string parameter that you use to initialize your editor contents. The contents can be modified after the editor is up.

contentType parameter

The code edit widget uses MIME types for syntax highlighting and language tooling. The parameter is a string that present a MIME type, such as "application/javascript". By default, the widget supports the syntax highlighting for the following MIME types.

  • "application/javascript"
  • "application/x-ejs"
  • "text/css"
  • "text/html"
  • "text/x-java-source"
  • "application/x-jsp"
  • "application/json"
  • "text/x-launch"
  • "text/x-jade"
  • "text/x-python"
  • "text/x-ruby"
  • "text/x-go"
  • "text/x-objective-c"
  • "text/x-php"
  • "text/x-swift"
  • "application/xml"
  • "application/xhtml+xml"
  • "text/x-yaml"
  • "text/x-arduino"
  • "text/x-csrc"
  • "text/x-c"
  • "text/x-csharp"
  • "text/x-cshtml"
  • "text/x-c++src"
  • "text/x-dockerfile"
  • "text/x-erlang"
  • "text/x-haml"
  • "text/x-lua"
  • "application/xquery"
  • "text/x-vb"
  • "text/x-vbhtml"

Theming your editor

As described in the previous sections, if you use the advanced way to create your editor you will get hold of an editorViewer instance. The fundamental class of an Orion editor is called TextView that is theme-able in different ways. If you call editorViewer.editor.getTextView() it returns you an instance of TextView. You can then call editorViewer.editor.getTextView().setOptions({themeClass: "editorTheme"}) to theme your editor. Note that "editorTheme" is the class selector that is defined in the following css, as the default theme that the widget is using. You can change any of the classes for your own theme.

/*Editor syntax highlighting themes*/
/*Refer to https://manual.macromates.com/en/language_grammars at section 12.4 for the theme hierarchy*/
.editorTheme .comment {
	color: #3C802C;
}
.editorTheme .constant.numeric.hex {
	color: #9932CC;
}
.editorTheme .constant.numeric {
	color: #9932CC;
}
.editorTheme .constant {
	color: #9932CC;
}
.editorTheme .entity.name.function {
	color: #67BBB8;
	font-weight: bold;
}
.editorTheme .entity.name {
	color: #98937B;
}
.editorTheme .entity.other.attribute-name {
	color: #5F9EA0;
}
.editorTheme .keyword.control {
	color: #CC4C07;
	font-weight: bold;
}
.editorTheme .keyword.operator {
	color: #9F4177;
	font-weight: bold;
}
.editorTheme .keyword.other.documentation.task {
	color: #5595ff;
}
.editorTheme .keyword.other.documentation {
	color: #7F9FBF;
}
.editorTheme .meta.documentation.annotation {
	color: #7F9FBF;
}
.editorTheme .meta.documentation.tag {
	color: #7F7F9F;
}
.editorTheme .meta.preprocessor {
	color: #A4A4A4;
}
.editorTheme .meta.tag.attribute {
	color: #93a2aa;
}
.editorTheme .meta.tag {
	color: #CC4C07;
}
.editorTheme .punctuation.operator {
	color: #D1416F;
}
.editorTheme .string.interpolated {
	color: #151515;
}
.editorTheme .string {
	color: #446FBD;
}
.editorTheme .support.type.propertyName {
	color: #9F4177;
}
.editorTheme .variable.language {
	color: #7F0055;
	font-weight: bold;
}
.editorTheme .variable.other {
	color: #E038AD;
}
.editorTheme .variable.parameter {
	color: #D1416F;
}
/*Editor core themes*/
.editorTheme .annotationLine.currentLine {
	background-color: #EAF2FE;
}
.editorTheme .annotationRange.currentBracket {
	background-color: #00FE00;
}
.editorTheme .annotationRange.matchingBracket {
	background-color: #00FE00;
}
.editorTheme .annotationRange.matchingSearch.currentSearch {
	background-color: #53d1ff;
}
.editorTheme .annotationRange.matchingSearch {
	background-color: #c3e1ff;
}
.editorTheme .annotationRange.writeOccurrence {
	background-color: #ffff00;
}
.editorTheme .ruler.annotations {
	background-color: #ffffff;
}
.editorTheme .ruler.overview {
	background-color: #ffffff;
}
.editorTheme .ruler {
	background-color: #ffffff;
}
.editorTheme .rulerLines {
	color: #CCCCCC;
}
.editorTheme .textviewContent ::-moz-selection {
	background-color: #b4d5ff;
}
.editorTheme .textviewContent ::selection {
	background-color: #b4d5ff;
}
.editorTheme .textviewLeftRuler {
	border-color: #ffffff;
}
.editorTheme .textviewRightRuler {
	border-color: #ffffff;
}
.editorTheme .textviewSelection {
	background-color: #b4d5ff;
}
.editorTheme .textviewSelectionUnfocused {
	background-color: #b4d5ff;
}
.editorTheme.textview {
	background-color: #ffffff;
	color: #151515;
	font-family: "Source Code Pro", "Consolas", "Monaco", "Vera Mono", monospace;
	font-size: 14px;
}

You can copy the above contents and paste into your own css file and load the css file into your page. Then change your code to call editorViewer.editor.getTextView().setOptions({themeClass: "editorTheme"}) , after editorViewer.setContents is called. Refer to the snippet below, assuming that you've created the css file called editorTheme.css and loaded into your page.

codeEdit.create({parent: "myEditorDivId"}).then(function(editorViewer) {
    editorViewer.setContents("var foo;", "application/javascript");
    editorViewer.editor.getTextView().setOptions({themeClass: "editorTheme"});
});

Theming the syntax highlighting

Refer to TextMate Language Grammars at section 12.4 Naming Conventions on how to change the css class values in the editorTheme.css file. For instance if you change one of the classes as below, your syntax highlighting on the javascript control keyword will become green from dark orange(default).

.editorTheme .keyword.control {
	color: green;/*#CC4C07;*/
	font-weight: bold;
}

Other options to customize your editor

The codeEdit.serviceRegistry instance

Warning2.png
The information on this section applies only to the code edit widget build after the 11.0 stream integration build I20151126-2218

Once you create the code edit instance, there is a service registry created under the widget. The service registry will be shared by all the editor viewers you will create later. Before you call codeEdit.creat() API, you can register your services to the widget first and they will be consumed properly by the editor viewers with related content type.

<link rel="stylesheet" type="text/css" href="editorBuild/code_edit/built-codeEdit.css"/>
<script src="editorBuild/code_edit/built-codeEdit.min.js"></script>
<script>
    window.onload=function(){
        var codeEdit = new orion.codeEdit();
        codeEdit.serviceRegistry.registerService(your service );
        codeEdit.create(...);
        codeEdit.create(...);
    };
</script>

The codeEdit.serviceRegistry is an instance of Orion serviceRegistry by which you can register your own services to extend your editor. Below is a sample snippet on how to register a simple(fake) content assist service on an xml file.

var contentAssistProvider = {
    computeProposals: function(buffer, offset, context) {
        var result = [];
        for(var i = 0; i < 10; i++){
            result.push("proposal " + i);
        }
        return result;
    }
};
editorViewer.serviceRegistry.registerService("orion.edit.contentassist",
    contentAssistProvider,
    { name: "xmlContentAssist",
      contentType: ["application/xml"]
    });

Note that you can register all your services against different content types, before you call create() API. Then when you call the create() API even multiple time, or, use the same editorViewer instance to call setContents multiple times, those registered services will be filtered according to the current content type.

Unregister a service instance

We do not recommend un-registering a service but if you have cases you really want to un-register it, refer to the following snippet.

var myServiceEntry = codeEdit.serviceRegistry.registerService(your service );
//.........
myServiceEntry.unregister();

The editorViewer promise

As mentioned in the previous section, when you call codeEdit.create API a promise object editorViewer is returned. There are 3 major instances wrapped by the editorViewer, by which you can use to customize your editor further.

  • editorViewer.serviceRegistry
  • editorViewer.editor
  • editorViewer.inputManager

The editorViewer.serviceRegistry instance

The editorViewer.serviceRegistry is pointing to the codeEdit.serviceRegistry.

The editorViewer.editor instance

The editorViewer.editor is an instance of Orion editor by which you can use to customize your editor in details. Below is a sample snippet on how to call the editor's APIs to set your editor's theme and disable some editor rulers.

editorView.editor.getTextView().setOptions({themeClass: "editorTheme"});
editorView.editor.setLineNumberRulerVisible(false);
editorView.editor.setAnnotationRulerVisible(false);
editorView.editor.setOverviewRulerVisible(false);
editorView.editor.setFoldingRulerVisible(false);

The editorViewer.inputManager instance

The editorViewer.inputManager is an instance of editor input manager by which you can use to customize the input for an editor. The most important API you may want to use is the setAutoSaveTimeout. By default the editorViewer.inputManager sets the autoSaveTimeOut as 300ms to trigger code validation. If you do not want to trigger code validation automatically use the code as below to turn it off.

editorViewer.inputManager.setAutoSaveTimeout(-1);

Passing user plugins

Warning2.png
The information on this section applies only to the code edit widget build after the 11.0 stream integration build I20151119-2218


You can pass a list of your own plugins by passing the userPlugins option when you initialize the codeEdit instance. Refer to understanding the Orion plugins on how to write your own plugins in general. Refer to plugging into the editor on how to inject different types of plugins into your editor.

var codeEdit = new orion.codeEdit({userPlugins: ["your plugin URL1", "your plugin URL2"]})
codeEdit.create({parent: "myEditorDivId").then(function(editorViewer) {
    editorViewer.setContents("var foo;", "application/javascript");
});

Note that the user plugins live together with the widget's built-in plugins. All the plugins including the user plugins are loaded lazily when the first time you call codeEdit.create API. If you create several editor instances in your page by calling the codeEdit.create API several times, the plugins are shared among editors.

Using the editorViewer.serviceRegistry

Instead of passing your own plugins during the widget construction, you can also register your plugins as services directly from the editorViewer.serviceRegistry. All the plugins described in plugging into the editor are plug-able after you call codeEdit.create API.

var codeEdit = new orion.codeEdit()
codeEdit.create({parent: "myEditorDivId").then(function(editorViewer) {
    editorViewer.serviceRegistry.registerService(your service );
    editorViewer.setContents("var foo;", "application/javascript");
});

Note that the editorViewer.serviceRegistry are shared among editors. If you have multiple editors, all editorViewer.serviceRegistry references are pointing to the same serviceregistry that is the codeEdit.serviceRegistry mentioned before.

Add your own content type

Warning2.png
The information on this section applies only to the code edit widget build after the 11.0 stream integration build I20151126-2218

If you have your own content types other than the default ones that the code edit widget offers, the first thing you want to do is to register your content type once the widget instance is created, before you call the create() API. Again you can achieve this by using either user plugins or codeEdit.serviceRegistry. Here is a complete snippet to do it from codeEdit.serviceRegistry:

var codeEdit = new orion.codeEdit();
var cto = {
	id: "text/myID",//use this MIME type ID to call create()
	extension: ["myExt"],//file extension
	name: 'My content type',
	'extends': 'text/plain'//Your type has to extends 'text/plain'
};
codeEdit.serviceRegistry.registerService('orion.core.contenttype', {}, {contentTypes: [cto]});

Note that you have to register your own content type before you can call codeEdit.create() API, because the create() API requires your content type.

Saving your editor contents

Code edit widget uses an underlying in-memory file system to handle all the file read and write. Unless you implement a real file system, the save action triggered by the CTRL+s or CMD+s will not save your contents automatically. Also, as a reserved key binding from the widget, CTRL+s is not only for the contents saving but also triggers auto-validation, etc. We do not recommend overriding the key binding so you will need to refer to the following snippet to simply accomplish the saving action without losing other features while saving.

var codeEdit = new orion.codeEdit()
codeEdit.create({parent: "myEditorDivId").then(function(editorViewer) {
    //If you do not want to use auto save feature then use the following line to disable it.
    editorViewer.inputManager.setAutoSaveTimeout(-1);
    //There is an event fired against editorViewer.editor every time when the editor contents changes. The evt.contentsSaved is a flag indicating a save action is required or not.
    editorViewer.editor.addEventListener("InputChanged", function(evt) {
        if(evt.contentsSaved) {
	    //Save your editor contents;
	}
    });
    //Remove the listener when you destruct the editorViewer.
    //editorViewer.editor.removeEventListener("InputChanged", yourListener);
});

Note that the widget's in-memory file system does not do the real save at all. The auto-save flag is turned on by default, which triggers the auto-validation. You may not want the auto-save and you only want validation based on CTRL+s or CMD+s. The Save your editor contents part can be asynchronous. Your implementation of the save action is completely separated from the Code Edit widget.

Frequent asked questions

How to provide a markdown type of hover service

Assume that you want to provide a hover service as shown below.

FAQ hoverExample.png

There you want your hover service title as bold and the contents of the hover information as paragraphs with bold, italic, etc. By default the hover title is styled as normal font but you can change it by overriding the css as below:

.textviewTooltip .hoverTooltipTitle {
    font-weight: bold;
}

For the different fonts and paragraphs of the contents, you can use the markdown specification. The snippet below shows how to achieve this as an example.

var hoverProvider = {
    computeHoverInfo: function (editorContext, context) {
        var pContent = "_This text will be italic_  \n\n__This text will be bold__  \nThis text is normal";
        return { title: "This is the title",
		 content: pContent,
		 type: "markdown"};
};
codeEdit.serviceRegistry.registerService("orion.edit.hover",
    hoverProvider,
    { name: "my hover service",
      contentType: ["text/myContentType"]
});

How to provide a simple content assist service

Assume that you want to provide a content assist service. Refer to the following snippet to provide a simple one.

var contentAssistProvider = {
    computeProposals: function(buffer, offset, context) {
        var result = [];
        for(var i = 0; i < 10; i++){
            result.push({proposal: "proposal " + i});
        }
        return result;
    }
};
codeEdit.serviceRegistry.registerService("orion.edit.contentassist",
    contentAssistProvider,
    {	name: "my content assit",
        contentType: ["text/myContentType"]
});

How to provide a content assist service with tool tips

In the previous two sections we talked about providing hover and content assist services. Now lets see how to integrate the two services together to provide a fancier content assist implementation that will render a tooltip on the right hand side of the selected proposal.

Add one more property into the proposal object

The best way to provide a tooltip for a content assist proposal is to add a hover property to the returned proposal. In the next step, your hover service provider will check each proposal for this hover property and use it to create the tool tip contents. The hover property must contain content and type properties. The most common type to use is markdown, but all of the types in the Hover API are supported. Your code will look like this:

var contentAssistProvider = {
    computeProposals: function(buffer, offset, context) {
        var result = [];
        for(var i = 0; i < 10; i++){
            result.push({proposal: "proposal " + i,
                         hover: {content: "my tool tip" + i, type: "markdown"}});
        }
        return result;
    }
};

Add special code into your hover service provider

The hover property you've just added to the proposal will be then rendered by your hover service. So your implementation of the hover service has to be changed a little bit to reflect the changes you've made on the proposal object. In the computeHoverInfo: function (editorContext, context) API, the selected proposal from content assist will be passed in the context parameter. So your new implementation of the hover service will look like below:

var hoverProvider = {
    computeHoverInfo: function (editorContext, context) {
	if(context.proposal && context.proposal.hover) {
	    return context.proposal.hover;
	}
        var pContent = "_This text will be italic_  \n\n__This text will be bold__  \nThis text is normal";
        return { title: "This is the title",
		 content: pContent,
		 type: "markdown"};
};

Now your hover service does two things for you: normal hover service and tool tips on the content assist.

Everything together

Your final code will look like this:

var hoverProvider = {
    computeHoverInfo: function (editorContext, context) {
	if(context.proposal && context.proposal.hover) {
	    return context.proposal.hover;
	}
        var pContent = "_This text will be italic_  \n\n__This text will be bold__  \nThis text is normal";
        return { title: "This is the title",
		 content: pContent,
		 type: "markdown"};
};
var contentAssistProvider = {
    computeProposals: function(buffer, offset, context) {
        var result = [];
        for(var i = 0; i < 10; i++){
            result.push({proposal: "proposal " + i,
                         hover: {content: "my tool tip" + i, type: "markdown"}});
        }
        return result;
    }
};
codeEdit.serviceRegistry.registerService("orion.edit.hover",
    hoverProvider,
    { name: "my hover service",
      contentType: ["text/myContentType"]
});
codeEdit.serviceRegistry.registerService("orion.edit.contentassist",
    contentAssistProvider,
    {	name: "my content assit",
        contentType: ["text/myContentType"]
});

How to turn on/off the readonly flag on the same editorViewer.editor instance

The readonly flag can be set after the editor creation but assume that you hold an editorViewer instance for the time being and want to turn on/off the readonly flag depending on what type of contents are reloaded into the same editorViewer.editor instance. You can use the two lines of API calls as show below:

editorViewer.readonly = true;
editorViewer.editor.getTextView().setOptions({"readonly": true});

How to highlight a line in the editor and set cursor on that line

Assume that you want to highlight a line in your editor with your own line and gutter style as below.

FAQ highlightLine.png

For example, when your editor is up you want to double click on the gutter of a certain line and highlight the gutter with red background color and dotted outline for the line of text. If you double click on an existing gutter, the gutter and the line highlight go away. Below is a completely run-able HTML file that shows you how to achieve this.

<!doctype html>
<html>
    <head>
		<meta name="copyright" content="Copyright (c) IBM Corporation and others 2010, 2014." >
		<meta http-equiv="Content-Language" content="en-us">
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<title>Pluggable Editor Demo</title>
		<style type="text/css">
			.lineHighlight{
				outline: 2px dotted black;
			}
			.lineHighlightGutter{
				background-color: red;
			}
			.demoTitle{
				border: none;
				vertical-align: middle;
				overflow: hidden;
				text-align: left;
				margin-left: 15%;
				margin-right: 15%;
				padding-bottom: 5px;
				position: relative;
			}
			.demoBody{
				border: 1px solid;
				vertical-align: middle;
				border-color: blue;
				overflow: hidden;
				text-align: left;
				margin-left: 15%;
				margin-right: 15%;
				margin-bottom: 15px;
				padding-bottom: 5px;
				position: relative;
				height: 450px;
			}
		</style>
	    <link rel="stylesheet" type="text/css" href="editorBuild/code_edit/built-codeEdit.css"/>
		<script src="editorBuild/code_edit/built-codeEdit.js"></script>
		<script>
			/*globals orion */
			function setupOnce(editorViewer) {
				editorViewer.editor.addEventListener("InputChanged", function(){ //$NON-NLS-0$
		 			/*
		 			 * Things you only need to do once per editor
		 			 */
		 			var editor = editorViewer.editor;
		 		 	//Get the line styler inside the editor
				 	var annoStyler = editor.getAnnotationStyler();
		 		 	//Add your annotation type to the editor 
		 		 	annoStyler.addAnnotationType("my.customize.linehighlight");
		 		 	//Add the same annotation type ot the annotation ruler(gutter)
		 		 	editor.getAnnotationRuler().addAnnotationType("my.customize.linehighlight");
		 		 	editor.getAnnotationRuler().onDblClick = function(lineIndex, e) {
						if (lineIndex === undefined) { return; }
						if (lineIndex === -1) { return; }
						var view = editor.getTextView();
						var viewModel = view.getModel();
						var annotationModel = editor.getAnnotationModel();
						var lineStart = editor.mapOffset(viewModel.getLineStart(lineIndex));
						var lineEnd = editor.mapOffset(viewModel.getLineEnd(lineIndex));
						var annotations = annotationModel.getAnnotations(lineStart, lineEnd);
						var gutter = null;
						while (annotations.hasNext()) {
							var annotation = annotations.next();
							if (annotation.type === "my.customize.linehighlight") {
								gutter = annotation;
								break;
							}
						}
		 		 		setCursorAndGotoLine(editorViewer, lineIndex, gutter);
		 		 	};
			 	});
			}
			function setCursorAndGotoLine(editorViewer, lineNumber/*zero based*/, gutter) {
		 		editorViewer.editor.onGotoLine(lineNumber, 0, 0, function() {
		 			var editor = editorViewer.editor;
		 		 	//annotationModel is the handler you add or remove you annotation models
		 		 	var annotationModel = editor.getAnnotationModel();
		  		 	if(!annotationModel){
				 		return;
		 		 	}
		 		 	//As the annotation model is a range that is based on the charater offset of the {star, end}, you have to use the textModel to calculate that)
		 		 	var textModel = editor.getTextView().getModel();
		 		 	var startIndex = textModel.getLineStart(lineNumber);
		 		 	var endIndex = textModel.getLineEnd(lineNumber);
		 		 	
		  		 	//Add and/or remove your annotation models
		 		 	//The first param is an array of the annotations you want to remove
		 		 	//The second param is an array of the annotations you want to add
		 		 	var firstParam = [], secondParam = [],
		 		 	param = [{
			 		 	start: startIndex,
			 		 	end: endIndex,
			 		 	title: "",
			 		 	type: "my.customize.linehighlight",
			 		 	html: "",
			 		 	style: {styleClass: "lineHighlightGutter"}, //Gutter style at the line		 		 		 		 
			 		 	lineStyle: {styleClass: "lineHighlight"} //The line style in the editor
		 		 	}];
		 		 	if(gutter) {
		 		 		firstParam = [gutter];
		 		 	} else {
		 		 		secondParam = param;
		 		 	}
		 		 	annotationModel.replaceAnnotations(firstParam, secondParam);
		 		});
			}
			window.onload=function(){
				var codeEdit = new orion.codeEdit();
				var contents = 'var foo = "bar";\n' +
									 "var bar = foo;\n" + 
									 "/*\n" + 
									 " * test demo\n" + 
									 "*/\n" + 
									 "function test(){\n" + 
									 "	var foo1 = bar.lastIndexOf(char, from);\n" + 
									 "}\n" + 
									"//Keep editting in this demo and try the content assit, probem validations and hover service!\n" +
									 "var foo2 = foo."; 
				codeEdit.create({parent: "embeddedEditor", contentType: "application/javascript", contents: contents}).then(function(editorViewer) {
					document.getElementById("progressMessageDiv").textContent = "Plugins loaded!";
					setupOnce(editorViewer);
				});
			};
		</script>
    </head>
	<body id="orion-browser" spellcheck="false" class="orionPage">
		<div class="demoTitle">
			<span id = "progressMessageDiv" style="color: green">Loading language tooling plugins...</span>
		</div>
		</div>
		<div class="demoBody" id="embeddedEditor">
		</div>
	</body>
</html>

How to iterate all the errors and warnings

If an editor has the validator service registered, there will be error and warning markers rendered in the editor. Assume that you want to walk through all the problems objects from the editor whenever there are problem changes. You can use the following snippet. Note that the markerService in the snippet is based per editor. If you have multiple editor instances in your page and you want to do the same thing for other editors, you have to listen to the "problemsChanged" event on other editors as well.

var markerService = editorViewer.serviceRegistry.getService(editorViewer.problemsServiceID);
if(markerService) {
    markerService.addEventListener("problemsChanged", function(evt) { //$NON-NLS-0$
        if(evt.problems) {
            evt.problems.forEach(function(problem) {
                console.log(problem);
	    });
        }
    });
}

There is another way to get all the errors and warnings as well but we do not recommend this unless you want to use it by your own. For example if you do not want the "problemsChanged" event to trigger your walk-through. Note that the setTimeout call is just an example way to show you how it works.

window.setTimeout(function() {
    var annotationModel = editorViewer.editor.getAnnotationModel();
    var annotations = annotationModel.getAnnotations();
    while(annotations.hasNext()) {
        var annotation = annotations.next();
        if(annotation.type === "orion.annotation.error" || annotation.type === "orion.annotation.warning") {
	    console.log(annotation);
	}
   }
}, 500);

How to change the editor's font dynamically

If you want to change the editor's font size dynamically please refer to following snippet. Assume that you have a function called changeFontDynamically() that you want to call from your action to increase the font size by 1px each time it is called.

var fontSizeCounter = 9;
var themeClass = "myTheme";
var settings = {
	"className": "myTheme",
	"name": "myTheme",
	"styles": {
		"fontSize": "9px"
	}
};
function changeFontDynamically() {
	var theme = editorViewer.editor.getTextView().getOptions("theme");
	settings["styles"]["fontSize"] = fontSizeCounter + "px";
	theme.setThemeClass(themeClass, theme.buildStyleSheet(themeClass, settings));
	fontSizeCounter++;
}

Note that this will change the font of all the editor instances if you have multiple editors in your page.

How to customize the editor configuration before creation

Warning2.png
The information on this section applies only to the code edit widget build after 12.0 stream stable build S20160503-1120(inclusive)

The code edit widget uses a json object to manage the editor configurations for all the editorViewer instances you will create. The default configuration shows as below.

	var defaults = {
		autoSave: true,
		autoSaveVisible: true,
		autoSaveLocalVisible: true,
		autoSaveTimeout: 250,
		autoSaveTimeoutVisible: true,
		themeVisible: true,
		themeLocalVisible: true,
		fontSizeVisible: true,
		fontSizeLocalVisible: true,
		autoLoad: true,
		autoLoadVisible: true,
		saveDiffs: true,
		saveDiffsVisible: true,
		contentAssistAutoTrigger: true,
		contentAssistAutoTriggerVisible: true,
		showOccurrences: true,
		showOccurrencesVisible: true,
		autoPairParentheses: true,
		autoPairParenthesesVisible: true,
		autoPairBraces: true,
		autoPairBracesVisible: true,
		autoPairSquareBrackets: true,
		autoPairSquareBracketsVisible: true,
		autoPairAngleBrackets: false,
		autoPairAngleBracketsVisible: true,
		autoPairQuotations: true,
		autoPairQuotationsVisible: true,
		autoCompleteComments: true,
		autoCompleteCommentsVisible: true,
		smartIndentation: true,
		smartIndentationVisible: true,
		trimTrailingWhiteSpace: false,
		trimTrailingWhiteSpaceVisible: true,
		tabSize: 4,
		tabSizeVisible: true,
		expandTab: false,
		expandTabVisible: true,
		scrollAnimation: true,
		scrollAnimationVisible: true,
		scrollAnimationTimeout: 300,
		scrollAnimationTimeoutVisible: true,
		annotationRuler: true,
		annotationRulerVisible: true,
		lineNumberRuler: true,
		lineNumberRulerVisible: true,
		foldingRuler: true,
		foldingRulerVisible: true,
		overviewRuler: true,
		overviewRulerVisible: true,
		zoomRuler: false,
		zoomRulerVisible: true,
		zoomRulerLocalVisible: true,
		showWhitespaces: false,
		showWhitespacesVisible: true,
		wordWrap: false,
		wordWrapVisible: true,
		showMargin: false,
		showMarginVisible: true,
		marginOffset: 80,
		marginOffsetVisible: true,
		keyBindings: "Default",
		keyBindingsVisible: true,
		keyBindingsLocalVisible: true,
		diffService: false,
		diffServiceVisible: false
	};

If you want to change any properties of the configuration, you can easily pass an editorConfig option to the widget construction. You only need to pass the properties that you want to change. For example you want to show the white space as special characters and show a zoom ruler(a wide ruler on the right of the editor to overview the whole editor and tells you where you are), the following snippet tells you how to do it.

<link rel="stylesheet" type="text/css" href="editorBuild/code_edit/built-codeEdit.css"/>
<script src="editorBuild/code_edit/built-codeEdit.min.js"></script>
<script>
    window.onload=function(){
        var codeEdit = new orion.codeEdit({
            editorConfig: {showWhitespaces: true, zoomRuler: true}
        });
        codeEdit .create(...).then(function(editorViewer1){});
        codeEdit .create(...).then(function(editorViewer2){});
        //editorViewer1 and editorViewer2 will use the same editor config.    
    };
</script>

Note that the editorConfig is passed in the codeEdit construction, not in the codeEdit.create API. All the editorViewer instances will share the same configuration.

How to pass a status reporter to the editor during its creation

Warning2.png
The information on this section applies only to the code edit widget build after 12.0 stream stable build S20160503-1120(inclusive)

The editorViewer is constantly feeding its status to the user of the widget. For example, the status like the (line, column) number to tell you where your caret is, "string not found" message when you use CTRL+F for search but not hitting a result. Theses status is fed to a callback function. We assume that each editor has its own status reporter. You can now pass a statusReporter option in the codeEdit.create API. In the multiple editor instances case if you want to share the status reporter, just pass the same function.

	var statusReporter = function(message, type, isAccessible) {
		var statusDiv = document.getElementById("statusBarDiv");
		if(!message || !statusDiv) {
			return;
		}
		if (type === "progress") {
			statusDiv.style = "color: green";
			statusDiv.textContent = "Progress message: " + message;
		} else if (type === "error") { //$NON-NLS-0$
			statusDiv.style = "color: red";
			statusDiv.textContent = "Error message: " + message;
		} else {
			statusDiv.style = "color: green";
			statusDiv.textContent = "Normal message: " + message;
		}
	};
	codeEdit.create({parent: "embeddedEditorID", 
					 statusReporter: statusReporter,
					 contentType: "application/javascript", 
					 contents: contents}).then(function(editorViewer) {});

Note that the statusReporter function is completely separate from the codeEdit widget. The "statusBarDiv" div in the example can be anything outside of the widget. You can decide if you want to share the reporter function among all the editorViewer instances or otherwise.

How to listen to the editor's mode changes after its creation

Warning2.png
The information on this section applies only to the code edit widget build after 12.0 stream stable build S20160503-1120(inclusive)

The editorViewer has several modes like the tab mode(CTRL+M to toggle), wrap mode(CTRL+ALT+W) and insert mode(insert key to toggle). If you want to listen to the mode changes and somehow display them, refer to the following snippet.

	editorViewer.editor.getTextView().addEventListener("Options",function(evt){
		if(evt.options) {
			if(evt.options.tabMode !== undefined) {
				//CTRL+m keys
				//True: you can tab inside the editor. False: Tab will get out of the editor DIV
				statusReporter("Tab mode has been changed to: " + evt.options.tabMode);
			} else if(evt.options.wrapMode !== undefined) {
				//CTRL+ALT+w keys
				statusReporter("Wrap mode has been changed to: " + evt.options.wrapMode);
			} else if(evt.options.overwriteMode !== undefined) {
				//Insert key
				statusReporter("Overwrite mode has been changed to: " + evt.options.overwriteMode);
			} else {
				statusReporter("Other options has been changed: ");
			}
		}
	});

Note that the event type is "Options" against editorViewer.editor.getTextView(), which is the core component in the widget. In the example above, we also use the statusReporter addressed in the previous section. If you do not want the mode status to be over written when your caret changes, you need another DIV to render the mode status.

To be continued...

This FAQ section is being actively maintained. If you have any other questions on using the Code Edit widget please feel free to ask Orion mailing list. We will generalize questions and answers and keep adding them here.

Back to the top