Posts for the month of April 2007

ErlyBird Screenshot: Including Args in Completion Suggestion

As a newbie to Erlang, I'm not familiar with those OTP module/functions, I have to go back to see the docs again and again. At least, now ErlyBird will suggest me the arguments of each function now.

Click on the picture to enlarge it

nn

Other progress:

  • If erl/erl.exe is under the environment path, ErlyBird will try to set Erlang Installation path automatically.
  • Indexing engine is now based on Lucene, and is integrated with NetBeans' Generic Scripting Framework 100%.
  • As I'm now using ErlyBird for my daily works, and have written more Erlang code, I fixed some bugs that cause ErlyBird unstable.

To do list in the near future:

  • Integrate with Erlware to provide better project manager.
  • Go to declaration of macro
  • Completion for fields of record
  • Highlighting unbound variables and unused variables
  • Basic re-factor features: rename local variable name, un-exported function name etc.
  • Manage Erlyweb project
  • Support syntax of Erlyweb template: *.et files
  • Stabilize, Stabilize

And I hope language engine of NetBeans will be optimized soon, since it eats too much memory and needs performance improvement.

From Rails to Erlyweb - Part I

Updated July 20 2007: new params.erl which uses dict to store params and converts to proper type(integer/float/string)

It's time to migrate our project from Rails to Erlyweb (It cost me one month to write an Erlang IDE before this happens :-)). I'll blog some tips of the procedure, focus on the differences between Rails and erlyweb.

1.How to handle params

Rails:
page = params.fetch(:p, 1).to_i
size = params.fetch(:s, @@RECORDS_PER_PAGE).to_i
Erlyweb:
-module(mymodel_controller)..
-define(RECORDS_PER_PAGE, 9).

list(A) ->
    P = params:from_yaws_arg(A)
    Page = params:get("p", P, 1), %% return integer
    Size = params:get("s", P, ?RECORDS_PER_PAGE),
    ....

You can also

Id = params:get("id", P, integer) %% return integer()
Id = params:get("id", P) %% return string()
%% Or, pass a fun to return the converted result
Id = params:get("id", P, fun string:to_integer/1) 
To get the above code working, I wrote a params.erl and placed under folder: apps\myapp\lib.
-module(params).

-export([
         from_yaws_arg/1,
         from_list/1,
         get_page_opts/1,
         get_page_extras/1,
	 get/2,
	 get/3,
         get/4,
         convert/2,
         put/3,
         join/1
        ]).

%% @doc Convert yaws Arg#arg.querydata into a {key,value} stored in a dynamic hash.
from_yaws_arg(Arg) ->
    Params = yaws_api:parse_query(Arg),
    dict:from_list(Params).

from_list(PropsList) ->
    dict:from_list(PropsList).

get_page_opts(Dict) ->
    [{page, get("p", Dict, 1)},
     {page_size, get("s", Dict, ?DEFAULT_PAGESIZE)}].

get_page_extras(Dict) ->
    Page = get("p", Dict, 1),
    Size = get("s", Dict, ?DEFAULT_PAGESIZE),
    Offset = (Page - 1) * Size, 
    [{limit, Offset, Size}].

get(Key, Dict) ->
    get(Key, Dict, undefined).

%% @spec get(Key::term(), Arg:yawsArg(), Default::term() -> string()
get(Key, Dict, Fun)  when is_function(Fun) ->
    case get(Key, Dict, undefined) of
        undefined -> undefined;
        Value     -> Fun(Value)
    end;
get(Key, Dict, Type) when is_atom(Type) andalso Type /= undefined -> 
    get(Key, Dict, undefined, Type);
get(Key, Dict, Default) ->
    case dict:find(Key, Dict) of
        {ok, Value} when is_list(Default)    -> convert(Value, string); 
        {ok, Value} when is_integer(Default) -> convert(Value, integer);
        {ok, Value} when is_float(Default)   -> convert(Value, float);
        {ok, Value} -> Value;
        error       -> Default
    end.
    
get(Key, Dict, Default, Type) ->
    Value = get(Key, Dict, Default),
    convert(Value, Type).

convert(Value, _Type) when Value == undefined -> undefined;
convert(Value,  Type) ->
    try
        case Type of
            integer when is_list(Value)    -> list_to_integer(Value);
            integer when is_integer(Value) -> Value;
            integer when is_float(Value)   -> erlang:round(Value);
            float   when is_list(Value)    -> list_to_float(Value);
            float   when is_integer(Value) -> Value * 1.0;
            float   when is_float(Value)   -> Value;
            string  when is_list(Value)    -> Value;
            string  when is_integer(Value) -> integer_to_list(Value);
            string  when is_float(Value)   -> float_to_list(Value);
            number  when is_list(Value) andalso Value /= "" andalso Value /= "NaN" ->
                case string:chr(Value, $.) > 0 of
                    true  -> list_to_float(Value);
                    false -> list_to_integer(Value)
                end;
            number  when is_integer(Value) -> Value;
            number  when is_float(Value)   -> Value
        end
    catch error:_ -> 
        undefined
    end.

put(Key, Value, Dict) ->
    dict:store(Key, Value, Dict).

join(Params) ->
    ParamPairedStrs =
        dict:fold(
            fun (Key, Value, Acc) ->
                    [Key ++ "=" ++ yaws_api:url_encode(Value)|Acc]
            end, [], Params),
    %% Join params string with "&": ["a=a1", "b=b1"] => "a=a1&b=b1"
    join("&", ParamPairedStrs).

%% @spec join(string(), list(string())) -> string()
join(String, List) -> join(String, List, []).
join(_String, [], Joined) -> Joined;
join(String, [H|[]], Joined) -> join(String, [], Joined ++ H);
join(String, [H|T],  Joined) -> join(String, T,  Joined ++ H ++ String).

Write an IDE in One Month - ErlyBird 0.11.0 Released

Updated Apr 24: The indexing feature is based on Lucene indexing from Common Scripting Framework now. But there won't be new release soon, since Generic Language Framework changes rapidly.

Updated Apr 21: There are several source files under \lib\megaco-3.5.3\src\text, which are with size > 300k, if you can not pass indexing procedure (ErlyBird hangs), please rename them something that are not end with ".erl" or ".hrl".

Updated Apr 20: Due to a severe bug that prevents setting Erlang Installation path, I've re-pack a new release 0.11.1 that fixed it. Don't forget to set the Erlang Installation path to full path of erl.exe or erl file, for instance, "C:\erl\bin\erl.exe".

Updated Apr 20: There is a bug, if your OTP is not installed as C:\erl, you can not set Erlang Installation. I'll update the bin package.

Updated Apr 20: erlybird-bin-0.11.0-ide.zip has been uploaded to sourceforge.net.

I'm newbie to IDE, to Erlang, to compilers principles. But, based on the works from NetBeans's guys, I wrote an almost complete Erlang IDE in one month.

With features:

  • Syntax checking.
  • Syntax highlighting.
  • Functions navigator.
  • Code-folding.
  • Indentation.
  • Completion for built-in/remote function call. (Ctrl+Space for suggestion) (new)
  • Go to declaration for remote function call (press down Ctrl and click on the function name). (new)
  • Project management - create/manage/build project tree. (new)
  • Compilation error locate. (new)
  • Auto indexing OTP libs and project sources. (new)
  • Interactive Erlang Console. (new)

And with known Issues:

  • ErlyBird is memory eager so far, it needs at least -Xmx256m set, 500m or more is recommended for big source files. Check the config file of NetBeans that is located at etc/netbeans.conf, make sure you've set -J-Xmx256m in line of 'netbeans_default_options'
  • Run project button does not work yet, if you press run project button, will show an interactive erlang shell only.
  • When indexing OTP libs, there may be Exceptions pop-ups, which indicate out of heap space, you can just ignore it.
  • Do not open/go to declaration too big source file in ErlyBird, this will also cause out of heap space.

And lacking features:

  • Debugging
  • ...

The parsing and editing features is based on Generic Language Framework.

The project management and indexing features is based on Common Scripting Framework.

To integrate editing feature, which is from Generic Language Framework, with Comman Scripting Framework, I had to lightly modify the Common Scripting Framework source code, the modified files are available from svn trunk folder: gsf-diff-ref.

And as I'm not yet familiar with Lucene index engine, which is used by Common Scripting Framework, I just wrote a sql db index engine and plug-in it to Common Scripting Framework. I will rewrite it based on LuceneIndex lately.

To download, please go to:

http://sourceforge.net/project/showfiles.php?group_id=192439

There are two installation options now, you can choose one of them:

The first one: A pre-packed NBMs kit: erlybird-bin-0.11.1-kit-nbms.zip(about 2.8M). To install:
Downloaded NetBeans IDE 6.0 M8+ via:
http://www.netbeans.info/downloads/dev.php
Select 'Milestone' in 'Build Type'.
After NetBeans IDE installed, unzip erlybird-bin-0.11.1-kit-nbms.zip first, then:

  1. From menu: Tools -> Update Center
  2. In the "Select Location of Modules" pane, click "Install Manually Downloaded Modules(.nbm Files)", then "Next"
  3. Click [Add...] button, go to the path to select all *.nbm files.
  4. Following the instructions to install updated modules.
  5. Restart NetBeans.
  6. Set your OTP path. From [Tools]->[Options], click on 'Miscellanous', then expand 'Erlang Installation', fill in the full path of your 'erl.exe' or 'erl' file, for instance: "C:/erl/bin/erl.exe"

The second one: A standalone ErlyBird IDE: erlybird-bin-0.11.1-ide.zip(about 18M). Notice: Please wait for me to upload it to sf.net :-) It does not need NetBeans IDE. To install:

  1. Just unzip it to somewhere, then execute 'bin/erlybird.exe' for windows, 'bin/erlybird' for *nix.
  2. Set your OTP path. From [Tools]->[Options], click on 'Miscellanous', then expand 'Erlang Installation', fill in the full path of your 'erl.exe' or 'erl' file, for instance: "C:/erl/bin/erl.exe"

If you are new to NetBeans, there are some docs for user:

http://www.netbeans.org/kb/55/quickstart.html
http://www.netbeans.org/kb/55/using-netbeans/index.html

It may not be stable yet, feedback and bug reports are welcome.

ErlyBird Screenshot: Completion/Go to Declaration for remote function call

ErlyBird now support completion for remote function call. And, if you click on the name of a remote function call, ErlyBird will open the remote module source file, and jump to the position of declaration. (Only OTP modules are supported currently)

The screenshot shows the popup with candidate functions of module "file", when you typing "file:"

I'll release a new version in one week.

Click on the picture to enlarge it

nn

ErlyBird Screenshot: Erlang Console and File Locator for Compile Errors

Updated Apr 11: copy/paste can be done via Ctrl+C/Ctrl+V.

The Erlang Shell console finally works on NetBeans, it works as same as on the shell/dos environment with historical commands feature. It still lacks copy/paste feature.

And ErlyBird supported Emakefile based project build. In the output window, you can click on the compiler error message to jump to the corresponding location of source file.

Click on the picture to enlarge it

nn

Erlang Project Support and Code Completion in ErlyBird

I've got the initial Erlang project management supported in ErlyBird, where the Erlang project tree can be newly created and managed by NetBeans. The code is ported from Tor's work for Ruby in NetBeans, and I'll try to sync my work with his work.

The Erlang interactive console does not truly work yet:-) but it's the first priority task.

Click on the picture to enlarge it

nn

Go to Declaration of Function call and Var in Erlang Editor for Netbeans

I've got "Go to declaration of function call and var" if the declarations are in the same module file, and "Highlighting for function call/function arguments" working.

To go to the declaration of function call or var, just press down "Ctrl", and put cursor on the function call or var name's position, then click on it. The editor will jump to the source position of declaration.

But to get cross-module "Go to declaration of function call" working, I may need much more works to do.

BTW, the Erlang project management is also under developing. Before this feature is released, the only method to create a managed project in NetBeans is create a Java project tree and use it.

Click on the picture to enlarge it

nn

Three Interviews with Language Programmers for NetBeans

Geertjan from NetBeans took three interviews with language programmers for NetBeans, one of them is me, where I talked about ErlyBird - Erlang Editor for NetBeans and AIOTrade (formly HumaiTrader) which are all based on NetBeans Platform.

About AIOTrade, in the interview, I said:

"I'm going to split AIOTrade into a client/server application. The server-side will act as a streaming quote data feed server and be responsible for delivering transaction orders to brokers in soft real-time, written in Erlang. The client-side charting and UI features will remain in Java, where Java is strong. With the Jinterface APIs provided by Erlang, Java is easily able to talk with the Erlang server."