Module wrangler

This module describes the refactorings that are currently supported by Wrangler.

Copyright © 2006-2009 Huiqing Li, Simon Thompson

Version: 0.7

Authors: Huiqing Li, Simon Thompson [web site: http://www.cs.kent.ac.uk/projects/forse].

Description

This module describes the refactorings that are currently supported by Wrangler.

Function Index

add_a_tag/6Add a tag to all the messages received by a server process.
duplicated_code_in_buffer/4A duplicated code detector that only works with the current Erlang buffer.
duplicated_code_in_dirs/4A duplicated code detector that works with multiple Erlang modules.
expression_search/4Search for clones of an expression/expression sequence selected in the current file.
fold_against_macro/5Fold expressions/patterns against a macro definition.
fold_expr/1Fold expressions against a function definition.
fun_extraction/5Introduce a new function to represent an expression or expression sequence.
fun_to_process/6Turn a function into a server process.
generalise/6 Generalise a function definition.
move_fun/7Move a function definition from its current module to another.
new_macro/6Introduce a macro to represent a syntactically well-formed expression/pattern or a sequence of expressions/patterns.
register_pid/6Register a process.
rename_fun/6Rename a function with a new name supplied by the user.
rename_mod/4Rename a module with a new name supplied by the user.
rename_mod_batch/4Rename a collection of modules in batch mode.
rename_process/6Rename a registered process with a new name supplied by the user.
rename_var/6Rename a variable with a new name supplied by the user.
tuple_funpar/6Turn some consecutive parameters of a function into a tuple parameter.
tuple_to_record/9From tuple to record representation.

Function Details

add_a_tag/6

add_a_tag(Filename::filename(), Line::integer(), Col::integer(), Tag::string(), SearchPaths::[dir()], TabWidth::integer()) -> {error, string()} | {ok, [filename()]}

Add a tag to all the messages received by a server process.

This refactoring should be initiated from the main receive function of a server process. The current implementation is still in an experimental stage, and has a number of limitations:

  • The current implementation assumes that the process does not send enquiries, using the send ... receive pattern, to other processes
  • The current implementation only handles processes spawned using spawn or spawn_link
  • Usage: to apply this refactoring, point the cursor to the function name, then select Add a Tag to Messages from the Refactor menu, Wrangler will then prompt to enter the tag.

    duplicated_code_in_buffer/4

    duplicated_code_in_buffer(FileName::filename(), MinToks::integer(), MinClones::integer(), TabWidth::integer()) -> {ok, string()}

    A duplicated code detector that only works with the current Erlang buffer.

    This function reports duplicated code fragments found in the current Erlang buffer, it does not remove those code clones though. The user will be prompted for two parameters: the minimum number of tokens a cloned code fragment should have, and the minimum number of times a code fragment appears in the program.

    The current version of the duplicated code detector reports clones that are syntactically identical after consistent renaming of variables, except for variations in literals, layout and comments.

    Usage: simply select the Detect Duplicated Code in Current Buffer from the Refactor sub-menu, Wrangler will prompt to input the parameters.

    duplicated_code_in_dirs/4

    duplicated_code_in_dirs(FileNameList::[filename() | dir()], MinToks::string(), MinClones::string(), TabWidth::integer()) -> {ok, string()}

    A duplicated code detector that works with multiple Erlang modules.

    This function reports duplicated code fragments found in the directories specified by SearchPaths, it does not remove those code clones though. The user will be prompted for two parameters: the minimum number of tokens that a cloned code fragment should have, and the minimum number of times a code fragment appears in the program.

    The current version of the duplicated code detector reports clones that are syntactically identical after consistent renaming of variables, except for variations in literals, layout and comments.

    Usage: first check the SearchPaths specificed in the customisation page to make sure that the directory (or directories) specified is the place where you want to search for duplicated code, then select Detect Duplicated Code in Dirs from the Refactor sub-menu, Wrangler will then prompt to input the parameters.

    expression_search/4

    expression_search(FileName::filename(), Start::Pos, End::Pos, TabWidth::integer()) -> {ok, [{integer(), integer(), integer(), integer()}]} | {error, string()}

    Search for clones of an expression/expression sequence selected in the current file.

    This functionality allows searching for clones of a selected expression or expression sequence. The found clones are syntactically identical to the code fragment selected after consistent renaming of variables, except for variations in literals, layout and comments.

    When the selected code contains multiple, but non-continuous sequence of, expressions, the first continuous sequence of expressions is taken as the user-selected expression. A continuous sequence of expressions is a sequence of expressions separated by ','.

    Usage: highlight the expression/expression sequence of interest, then selected Expression Search from the Refactor sub-menu.

    fold_against_macro/5

    fold_against_macro(FileName, Line, Col, SearchPaths, TabWidth) -> any()

    Fold expressions/patterns against a macro definition.

    This refactoring replaces instances of the right-hand side of a macro definition by the corresponding left-hand side with necessary parameter substitutions.

    To apply this refactoring, first point the cursor to the macro definition against which candidate expressions/candidates will be folded; then select Fold Against Macro Definition from the Refactor menu; after that, Wrangler will search the current module for expressions/patterns which are instances of the right-hand side of the selected macro definition; and direct you through the refactoring process.

    fold_expr/1

    fold_expr(X1::{{FileName::filename(), Line::integer(), Col::integer(), SearchPaths::[dir()], TabWidth::integer()} | {FileName::filename(), ModName::modulename(), Arity::integer(), ClauseIndex::integer(), SearchPaths::[dir()], TabWidth::integer()}}) -> {ok, [{{{integer(), integer()}, {integer(), integer()}}, syntaxTree()}]} | {error, string()}

    Fold expressions against a function definition.

    This refactoring replaces instances of the right-hand side of a function clause definition by the corresponding left-hand side with necessary parameter substitutions. The function clause can be defined in either the current module or another module.

    In the case that a candidate expression/expression sequence needs to export some variables which are used by the code following code, that expression/expression sequence will be replaced by a match expression, whose left-hand side it the exported variable(s), and right-hand side is the function application.

    This refactoring does not support folding against function clauses with guard expressions, or function clauses with complex formal parameters, such as tuples, lists, or records.

    Usage: first point the cursor to the function clause against which expressions will be folded if the function is defined in the current module, or leave the cursor anywhere if you would like to fold against a function clause defined in another module; then select Fold Expression Against Function from the Refactor menu; after that, Wrangler then asks to confirm that you want to fold against the function clause pointed to by the cursor, if you answer 'no', Wrangler will prompt to provide the module name, function name ,arity of the function and the index of the function clause (starting from 1). After all these initial interaction, Wrangler will search the current module for expressions which are instances of the right-hand side of the selected function clause.

    If no candidate expression has been found, a message will be given, and the refactoring finishes; otherwise, Wrangler will go through the found candidate expressions one by one, and ask the user whether she/he wants to replace the expression with an application of selected function. If the user answers 'yes' to one instance, that instance will be replaced by function application, otherwise it will remain unchanged.

    fun_extraction/5

    fun_extraction(FileName::filename(), Start::Pos, End::Pos, FunName::string(), TabWidth::integer()) -> {error, string()} | {ok, string()}

    Introduce a new function to represent an expression or expression sequence.

    This refactoring allows the user to introduce a new function to represent a selected expression or expression sequence, and replace the selected code with a call to the new function. Free variables within the selected code become the formal parameters of the function definition.

    Usage: highlight the expression/expression sequence of interest, then selected the Function Extraction from the Refactor sub-menu, Wrangler will then prompt for the new function name.

    fun_to_process/6

    fun_to_process(FileName::filename(), Line::integer(), Col::integer(), ProcessName::string(), SearchPaths::[dir()], TabWidth::integer()) -> {ok, [filename()]} | {error, string()}

    Turn a function into a server process.

    This refactoring turns a function into a server process, and all the function calls to this function into process communication. Turning a function into a server process provides potential for memorisation of calculated values, adding states to the process, etc.

    The following example shows the application of this refactoring to the function f/2 on the left-hand side, and the result is shown on the right-hand side.

             f(add,X,Y) -> X +Y;                        f_rpc(RegName, Request) ->
             f(sub,X,Y) -> X - Y.                            Fun = fun() ->
                                                                         try
             g(X, Y) ->                                                     register(RegName, self())
                 f(add, X,Y)*f(sub, X, Y).                               catch
                                                                            true -> f();
                                                                            error:_ -> already_running
                                                                         end
                                                                   end,
                                                             Spawn(Fun),
                                                             RegName ! {self(), Request},
                                                             receive {RegName, Response} -> Response end.
     
                                                        f() ->
                                                           receive
                                                              {From, {add, X, Y}} -> From ! {f, X + Y}, f();
                                                              {From, {sub, X, Y}} -> From ! {f, X - Y}, f()
                                                           end.
     
                                                        g(X, Y) ->
                                                           f_rpc(f, {add, X, Y}) * f_rpc(f, {sub, X, Y}).

    The following side-conditions apply to this refactoring:

  • The process name provided by the user should be lexically legal, and not conflict with existing process names.
  • The function should not be a recursive function, either directly or indirectly.
  • The current function or functions called by this function should not register the Pid returned by self().
  • Wrangler generates the new function name and the rpc function name automatically, but the user could always rename it afterwards. Suppose the original function is f/n, then the new function name would be f/0 and the rpc function name would be f_rpc/2; if any conflicts occur, '_i' will be attached to the end of the function name where i is a smallest number that makes the name fresh.

    To apply this refactoring, point the cursor to the function of interest, then select From Function to Process from the Refactor sub-menu, after that, Wrangler will prompt to enter the process registration name in the mini-buffer.

    generalise/6

    generalise(FileName::filename(), Start::Pos, End::Pos, ParName::string(), SearchPaths::[dir()], TabWidth::integer()) -> {ok, string()} | {error, string()}

    Generalise a function definition.

    Generalise a function definition by selecting a sub-expression of its right-hand side and making this the value of a new argument added to the definition of the function. The sub-expression becomes the actual parameter at the call sites.

    Here is an example of generalisation, in which the function add_one defined on the left-hand side is generalised on the expression 1 , and the result is shown on the right-hand side.

                    -module (test).                          -module (test).
                    -export([f/1]).                          -export([f/1]).
     
                    add_one ([H|T]) ->                       add_one (N, [H|T]) ->
                       [H+1 | add_one(T)];                      [H+N | add_one(N,T)];
                    add_one ([]) -> [].                      add_one (N, []) -> [].
     
                    f(X) -> add_one(X).                      f(X) -> add_one(1,X)

    In the case that the selected expression has a side-effect, the refactorer will wrap this expression in an function expression before passing it as the actual parameter to the call-sites. This is illustrated in the following example, in which function repeat/1 is generalised on the expression io:format("Hello\n").

                    -module (test).                          -module (test).
                    -export([f/0]).                          -export([f/0]).
     
                    repeat(0) -> ok;                         repeat(A, 0) -> ok;
                    repeat(N) ->                             repeat(A, N) ->
                      io:format("Hello\n"),                    A( ),
                      repeat(N-1).                             repeat(A,N-1).
     
                    f() -> repeat(5).                        f( ) ->
                                                                repeat (fun( )->io:format ("Hello\n") end, 5).

    This refactoring only affects the module in which the refactoring is initialised. In the case that the generalised function is exported by the module, an auxiliary function will be created to wrap the generalised function up, so that the module's interface is not changed.

    The following side-conditions apply to this refactoring:

  • Suppose the function to be generalised is foo/n , then foo/n+1 should not be in scope before the generalisation;
  • The new parameter name provided by the user should not conflict with the existing parameters or change the semantics of the function to be generalised.
  • Usage: to apply this refactoring, highlight the expression first, then select Generalise Function Definition from the Refactor menu, after that the refactorer will prompt to enter the parameter name in the mini-buffer.

    move_fun/7

    move_fun(FileName::filename(), Line::integer(), Col::integer(), TargetModName::string(), CreateNewFile::boolean(), SearchPaths::[dir()], TabWidth::integer()) -> {ok, [filename()]} | {error, string()}

    Move a function definition from its current module to another.

    This refactoring has a global effect, i.e., it affects all the modules in which the function is imported/used.

    This refactoring assumes that an Erlang module name always matches its file name.

    Suppose we move function foo/n from its current module M to module N , then the following side-conditions apply to this refactoring:

  • If foo/n is already in scope in module N , then its defining module should be M .
  • Function foo/n should not contain any uses of implicit fun expressions (Note: move a collection of functions together to another module will be supported by another refactoring).
  • Usage: to apply this refactoring, point the cursor at the function definition, then select Move Definition to Another Module from the Refactor menu, Wrangler will then prompt to enter the target module name in the mini-buffer.

    new_macro/6

    new_macro(FileName::filename(), Start::pos(), End::pos(), NewMacroName::string(), SearchPaths::[dir()], TabWidth::integer()) -> {error, string()} | {ok, string()}

    Introduce a macro to represent a syntactically well-formed expression/pattern or a sequence of expressions/patterns.

    This refactoring allows the user to define a new macro to represent a expression/pattern or sequence of of expressions/patterns selected by the user, and replace the selected code with an application of the macro. Free variables within the selected code become the formal parameters of the macro definition.

    register_pid/6

    register_pid(Filename::filename(), Start::pos(), End::pos(), RegName::string(), SearchPaths::[dir()], TabWidth::integer()) -> {error, string()} | {ok, [filename()]}

    Register a process.

    This refactoring registers a process id, Pid say, with a name, regname say, and replaces the uses of Pid ! Msg with regname ! Msg whenever it is possible.

    The following side-conditions apply to this refactoring:

  • The process name provided by the user should be lexically valid.
  • The name provided by the user should not have been used as a process name.
  • The process under consideration should not have been registered.
  • Only one process spawned by the spawn expression selected should exist anytime during the running of the system.
  • Usage: First select a match expression whose left-hand side is a process identifier, and right-hand side is a spawn expression, then select Register a Process command from the Refactor menu, after that, Wrangler will prompt for the process name.

    rename_fun/6

    rename_fun(FileName::filename(), Line::integer(), Col::integer(), NewName::string(), SearchPaths::[dir()], TabWidth::integer()) -> {error, string()} | {ok, [filename()]}

    Rename a function with a new name supplied by the user.

    When renaming an exported function, this refactoring has a global effect, that is, it affects all those modules in which this function is imported/used.

    The following side-conditions (or pre-conditions } apply to this refactoring:

  • The new function name should not cause confliction with any of the functions which are in scope in the current module;
  • In the case that the function to be renamed is imported by another module, the new function name (with the same arity) should not be already in scope (either defined or imported) in that module.
  • Usage: to apply this refactoring, point the cursor to any occurrence of this function name, then select Rename Function Name from the Refactor menu, after that, Wrangler will prompt to enter the new function name in the mini-buffer.

    rename_mod/4

    rename_mod(FileName::filename(), NewName::string(), SearchPaths::[dir()], TabWidth::integer()) -> {error, string()} | {ok, [filename()]}

    Rename a module with a new name supplied by the user.

    This refactoring affects all those modules in which the module name is used.

    The following side-conditions apply to this refactoring:

  • The new module name should not have been used as a module name in the program under consideration.
  • This refactoring assume that the file basename is always the same as the module name, therefore this refactoring changes the filename as well.
  • Usage: to apply this refactoring, point the cursor anywhere in the module to be renamed, then select Rename Module Name from the Refactor menu, after that, the refactorer will prompt to enter the new module name in the mini-buffer.

    rename_mod_batch/4

    rename_mod_batch(OldNamePattern::string(), NewNamePattern::string(), SearchPaths::[dir()], TabWidth::integer()) -> {ok, string()} | {error, string()}

    Rename a collection of modules in batch mode.

    This refactoring has a global effect.

    The following side-conditions apply to this refactoring:

  • The new module names should not conflict with each other, or any existing module names in the same scope which will not be renamed.
  • This refactoring assumes that the file basename is always the same as the module name, therefore filenames will be changed along with module names.
  • Usage: this refactoring is supposed to be run from the Erlang shell. For example, to rename all those module names which match the regular expression "foo_*" to "foo_*_1_0" in the directory c:/wrangler/test , just type the following command: wrangler:rename_mod_batch("foo_*, "foo_*_1_0", ["c:/wrangler/test"]) .

    rename_process/6

    rename_process(FileName::filename(), Line::integer(), Col::integer(), NewName::string(), SearchPaths::[dir()], TabWidth::integer()) -> {error, string()} | {undecidables, string()} | {ok, [filename()]}

    Rename a registered process with a new name supplied by the user.

    To apply this refactoring, point the cursor to the process name, then select Rename a Process from the Refactor menu, after that, Wrangler will prompt to enter the new process name in the mini-buffer.

    This refactoring has a global effect, i.e. it needs to check the whole program for places where the original process name is used.

    The following side-conditions apply to this refactoring:

  • The new process name should not be the atom 'undefined'
  • The new process name should not have been used as a process name in the program under consideration.
  • Since there are some cases where Wrangler cannot infer whether an atom represents a process name or not, for example, a process name in a message, it would be help the refactoring process to select the process name from expressions, such as registration expressions, where it is easier for Wrangler to infer that the atom is a process name.
  • rename_var/6

    rename_var(FileName::filename(), Line::integer(), Col::integer(), NewName::string(), SearchPaths::[dir()], TabWidth::integer()) -> {error, string()} | {ok, string()}

    Rename a variable with a new name supplied by the user.

    This refactoring has a local effect, i.e., it only affects the function clause in which the refactoring is initialised.

    The following side-conditions (or pre-conditions) apply to this refactoring:

  • The new variable name should not conflict with any of the declared variable names in the same scope;
  • The new variable name should not shadow any of the existing variables in the outer scopes, or be shadowed by any of of existing variables in the inner scopes, i.e., renaming to the new name should not change the semantics of the program.
  • Usage: to apply this refactoring, point the cursor to any occurrence of this variable, then select Rename Variable Name from the Refactor sub-menu, after that, Wrangler will prompt to enter the new variable name in the mini-buffer.

    tuple_funpar/6

    tuple_funpar(FileName::filename(), Line::integer(), Col::integer(), Number::string(), SearchPaths::[dir()], TabWidth::integer()) -> {error, string()} | {ok, [filename()]}

    Turn some consecutive parameters of a function into a tuple parameter.

    When the function under consideration is exported by the module where it is defined, this refactoring has a global effect.

    Suppose the new function after refactoring is f/n, then the following side-conditions apply:

  • f/n should not cause confliction with any of the functions which are in scope in the current module;
  • In the case that the function is imported by another module, then f/n should not be already in scope (either defined or imported) in that module.
  • Usage: to apply this refactoring, point the cursor to the parameter which is going to be the first element of the tuple, then select Tuple Function Arguments from the Refactor sub-menu, after that, Wrangler will prompt to enter the number of parameters to include into the tuple in the minibuffer.

    tuple_to_record/9

    tuple_to_record(File::filename(), FLine::integer(), FCol::integer(), LLine::integer(), LCol::integer(), RecName::string(), FieldString::[string()], SearchPaths::[dir()], TabWidth::integer()) -> {error, string()} | {ok, [filename()]}

    From tuple to record representation. NOTE: this refactoring is still in an experimental stage.

    This refactoring has a global effect, i.e., it affects all those modules in which this function is imported/used.

    The following side-conditions apply to this refactoring:

  • The record and field names must be lexically legal;
  • The number of record fields must equal to the selected tuple size;
  • The function must be defined in the current module;
  • The selected part must be a tuple.
  • To apply this refactoring, highlight the tuple, which should be a function parameter, then select From Tuple To Record from the Refactor sub-menu, Wrangler will then prompt to enter the record name and the record field names.


    Generated by EDoc, Mar 19 2009, 13:06:01.