== Scriptable !RefactorErl interface == ris is similar to [[ErlangShellInterface|ri]], with the following basic ideas: \\ * results are always returned via the function return value. \\ * no mandatory standard output. \\ * arguments very regular - semantic queries for almost everything. \\ * you can also input a semantic query via atoms instead of strings to ease escaping.\\ * operations are composable (i.e., continue one where another has left off) - queries and refactorings can go back and forth. \\ * you can perform a series of batch refactorings in a single step by selecting multiple entities at once. \\ === File manipulations === {{{ #!erlang % Here an entity is returned, which can be used later. Added = ris:add_byname("path/to/mymod.erl"). ... % Readd the file. ReAdded = ris:add(Added). }}} It is possible to drop files from the database: {{{ #!erlang ris:drop('mods[name==mymod]'). % or % 'Added' is the previous entity. ris:drop(Added). }}} === Refactorings === Transformations are listed in [[RefactoringSteps|refactoring functionalities]]. Here is the list of transformations that you can use via this interface: * '''eliminate/1''': Eliminates the given variable(s). ([[RefactoringSteps/EliminateVariable|eliminate variable]]). \\ * '''extfun/2, extract/2''': This is the [[RefactoringSteps/ExtractFunction|extract function]] transformation. \\ * '''inline/1''': Inlines the given function(s), or macro(s). ([[RefactoringSteps/InlineFunction|inline function]], [[RefactoringSteps/InlineMacro|inline macro]]). \\ * '''move/2''': Moves the given [[RefactoringSteps/MoveFunction|function]], [[RefactoringSteps/MoveMacro|macro]], or [[RefactoringSteps/MoveRecord|record]] into another module. {{{ #!erlang % Move every unexported function to another_module and move it back: ris:move( ris:move("mods[name=='mymod'] .fun[exported==false]", "other_mod"), "mymod"). }}} * '''rename/2''': Renames [[RefactoringSteps/RenameVariable|variable]], [[RefactoringSteps/RenameFunction|function]], [[RefactoringSteps/RenameRecord|record]], [[RefactoringSteps/RenameRecordField|record field]], [[RefactoringSteps/RenameMacro|macro]], [[RefactoringSteps/RenameHeader|header file]] or [[RefactoringSteps/RenameModule|module file]]. {{{ #!erlang ris:rename("mods[name=='mymod'].fun[name=='Colour']", "Color"). }}} {{{#!comment '''reorder/2''' Reorders the function parameters. ([[RefactoringSteps/ReorderParameters|reorder function paameters]]) Example: {{{ #!erlang ris:reorder("mod.fun[name==fun1,arity==3]", [3,1,2]). }}} Let's see some examples: Rename a function (Of course, not just functions can be renamed): {{{ #!erlang ris:rename("mods[name=='mymod'].fun[name=='Colour']", "Color"). }}} Move every unexported function to another_module and move it back: {{{ #!erlang ris:move( ris:move("mods[name=='mymod'] .fun[exported==false]", "other_mod"), "mymod"). }}} In every modul, reorder every functions' parameters, whose name is fun1 and the arity is 3: {{{ #!erlang ris:tupfun("mod[name==mod1].fun[name==fun1,arity==3]", {1,2}). }}} {{{ ris:generalize("mod[name==china].fun[name==sum].var[name=="A"]"). ris:eliminate("mod[name==china].fun[name==sum].var[name=="A"]"). ris:inline("mod[name==china].fun[name==sum] .expr[type/=pattern and index==1]"). }}} }}} == Operators == The result of the queries can be combined with the following set operators: \\ * '''intersect''': The following example takes the intersection of the files included by the two modules. {{{ #!erlang ris:q({"mods[name==mod1].includes", intersect, "mods[name==mod2].includes"}). }}} * '''union''': The union set operation. (Example: same as above, just substitute 'intersect' with 'union') \\ * '''minus''': The substraction set operation. (Example: same as above, just substitute 'intersect' with 'minus') \\ * '''Sequence''': Queries can be sequenced to continue a query from where another has left off. This example first adds the module from file 'mymodule.erl'. The add call returns the entities loaded. A semantic query aggregate of a list works by executing the first query (or in this case, specifying a starting entity), and then running the next query in the chain (in this case getting the name of files included by the add call). {{{ #!erlang ris:q([ris:add_byname("mymodule.erl"),".includes.name"]). }}} {{{#!comment * '''range''': Ranges of expressions can be selected which denotes a list of continuous expressions between two syntactical siblings. An empty semantic query denotes the beginning or end of a block when used as the initial or final limit respectively. In this example, the expression range starts from the (first) match expression that contains "Var1" in the pattern side up to the end of the syntactical block. {{{ NewFun = ris:extract({"mods[name=mod1] .fun[name==f and arity==0] .expr[type==match_expr] .esub[class==pattern and type==variable and .var[name=="Var1"]]", range, ""}, mynewfun). }}} * '''Another sequence example''': The following example shows an example for composition. It first renames all functions whose name is 'duck' to 'quack'. It then appends the suffix '_quacker' to the name of all functions which call these quacks. The new name is automatically converted to an atom. {{{ New = ris:rename('mods.fun[name==duck]',quack), Callers = ris:q([New,".callers"]), [ris:rename(Fun,atom_to_list(Name)++"_quacker") || Fun <- Callers, Name <- ris:q([Fun,".name"])]. New = ris:rename('mods.fun[name==duck]',quack), Callers = ris:q([New,".callers"]), ris:rename(Callers, fun(Fun)-> ris:qstr([Fun,".name"]) ++ "_quacker" end). }}} }}} == Textual display == Use '''ris:show/1''' to stringify entities. '''ris:show/2''' does the same while accepting additional options already known for '''ri:q/3'''. Use the respective '''ris:print/1''' and '''ris:print/2''' functions for screen and file output. {{{ #!erlang ris:print(ris:q("mods.fun")). }}} The following gives the same result set, but written to the given file and annotated with line numbers. (Note that you could also manually write the output of '''ris:show/1''' to a file.) {{{ #!erlang ris:print(ris:q("mods.fun"), [{out,"funs.txt"}, linenum]). }}}