/* * Copyright 2012-2014 Sergey Ignatov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.intellij.erlang.documentation; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkModificator; import com.intellij.openapi.roots.JavadocOrderRootType; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor; import org.intellij.erlang.psi.ErlangFunction; import org.intellij.erlang.psi.ErlangModule; import org.intellij.erlang.psi.ErlangTypeDefinition; import org.intellij.erlang.sdk.ErlangSdkRelease; import org.intellij.erlang.sdk.ErlangSdkType; import org.intellij.erlang.utils.ErlangLightPlatformCodeInsightFixtureTestCase; import org.jetbrains.annotations.NotNull; import java.util.List; @SuppressWarnings("ConstantConditions") public class ErlangDocumentationProviderTest extends ErlangLightPlatformCodeInsightFixtureTestCase { private ErlangDocumentationProvider myErlangDocProvider; @Override public void setUp() throws Exception { super.setUp(); myErlangDocProvider = new ErlangDocumentationProvider(); setUpProjectSdk(); } public void testExternalUrlSdkFunction() { doTestGetUrls("http://www.erlang.org/documentation/doc-5.9.2/lib/stdlib-1.18.2/doc/html/lists.html#foreach-2", "-module(test).\n" + "test() ->\n" + " lists:for<caret>each(foo, bar).\n"); } public void testExternalUrlSdkBif() { doTestGetUrls("http://www.erlang.org/documentation/doc-5.9.2/lib/stdlib-1.18.2/doc/html/lists.html#member-2", "-module(test).\n" + "test() ->\n" + " lists:mem<caret>ber(foo, bar).\n"); } public void testExternalUrlSdkModule() { doTestGetUrls("http://www.erlang.org/documentation/doc-5.9.2/lib/stdlib-1.18.2/doc/html/lists.html", "-module(test).\n" + "test() ->\n" + " lis<caret>ts:foreach(foo, bar).\n"); } public void testGenerateDocSdkBif() { doTestExternalDoc( "<html>\n" + ErlangSdkDocProviderBase.HTTP_STYLE + "<body>\n" + " <p><a name=\"member-2\"><span class=\"bold_code\">member(Elem, List) -> boolean()</span></a><br></p><div class=\"REFBODY\">\n" + "<p>Types:</p>\n" + " <div class=\"REFTYPES\">\n" + "<span class=\"bold_code\">Elem = term()</span><br>\n" + "</div>\n" + " <div class=\"REFTYPES\">\n" + "<span class=\"bold_code\">List = [term()]</span><br>\n" + "</div>\n" + " </div>\n" + "<div class=\"REFBODY\"><p>\n" + " <p>Returns <span class=\"code\">true</span> if <span class=\"code\">Elem</span> matches some element of\n" + " <span class=\"code\">List</span>, otherwise <span class=\"code\">false</span>.</p>\n" + " </p></div>\n" + "</body></html>\n", "" + "-module(test).\n" + "test() ->\n" + " lists:mem<caret>ber(foo, bar).\n"); } public void testGenerateDocSdkFunction() { doTestExternalDoc( "<html>\n" + ErlangSdkDocProviderBase.HTTP_STYLE + "<body>\n" + " <p><a name=\"foreach-2\"></a><span class=\"bold_code\">foreach(Fun, List) -> ok</span><br><div class=\"REFBODY\"><p>Types:</p>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Fun = fun((Elem :: T) -> term())</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">List = [T]</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">T = term()</span></div>\n" + "</div></p>\n" + "<div class=\"REFBODY\"><p>\n" + " <p>Calls <span class=\"code\">Fun(Elem)</span> for each element <span class=\"code\">Elem</span> in\n" + " <span class=\"code\">List</span>. This function is used for its side effects and\n" + " the evaluation order is defined to be the same as the order\n" + " of the elements in the list.</p>\n" + " </p></div>\n" + "</body></html>\n", "" + "-module(test).\n" + "test() ->\n" + " lists:for<caret>each(foo, bar).\n"); } public void testGenerateDocSdkFunctionMulti() { doTestExternalDoc( "<html>\n" + ErlangSdkDocProviderBase.HTTP_STYLE + "<body>\n" + " <p><a name=\"seq-2\"></a><span class=\"bold_code\">seq(From, To) -> Seq</span><br><a name=\"seq-3\"></a><span class=\"bold_code\">seq(From, To, Incr) -> Seq</span><br><div class=\"REFBODY\"><p>Types:</p>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">From = To = Incr = integer()</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\"></span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\"></span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Seq = [integer()]</span></div>\n" + "</div></p>\n" + "<div class=\"REFBODY\"><p>\n" + " <p>Returns a sequence of integers which starts with <span class=\"code\">From</span>\n" + " and contains the successive results of adding <span class=\"code\">Incr</span> to\n" + " the previous element, until <span class=\"code\">To</span> has been reached or\n" + " passed (in the latter case, <span class=\"code\">To</span> is not an element of\n" + " the sequence). <span class=\"code\">Incr</span> defaults to 1.</p>\n" + " <p>Failure: If <span class=\"code\">To<From-Incr</span> and <span class=\"code\">Incr</span>\n" + " is positive, or if <span class=\"code\">To>From-Incr</span> and <span class=\"code\">Incr</span> is\n" + " negative, or if <span class=\"code\">Incr==0</span> and <span class=\"code\">From/=To</span>.</p>\n" + " <p>The following equalities hold for all sequences:</p>\n" + " <div class=\"example\"><pre>\n" + "length(lists:seq(From, To)) == To-From+1\n" + "length(lists:seq(From, To, Incr)) == (To-From+Incr) div Incr</pre></div>\n" + " <p>Examples:</p>\n" + " <div class=\"example\"><pre>\n" + "> <span class=\"bold_code\">lists:seq(1, 10).</span>\n" + "[1,2,3,4,5,6,7,8,9,10]\n" + "> <span class=\"bold_code\">lists:seq(1, 20, 3).</span>\n" + "[1,4,7,10,13,16,19]\n" + "> <span class=\"bold_code\">lists:seq(1, 0, 1).</span>\n" + "[]\n" + "> <span class=\"bold_code\">lists:seq(10, 6, 4).</span>\n" + "[]\n" + "> <span class=\"bold_code\">lists:seq(1, 1, 0).</span>\n" + "[1]</pre></div>\n" + " </p></div>\n" + "</body></html>\n", "" + "-module(test).\n" + "test() ->\n" + " lists:s<caret>eq(foo, bar, blah).\n"); } public void testGenerateDocSdkLastFunction() { doTestExternalDoc( "<html>\n" + ErlangSdkDocProviderBase.HTTP_STYLE + "<body>\n" + " <p><a name=\"zipwith3-4\"></a><span class=\"bold_code\">zipwith3(Combine, List1, List2, List3) -> List4</span><br><div class=\"REFBODY\"><p>Types:</p>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Combine = fun((X, Y, Z) -> T)</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">List1 = [X]</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">List2 = [Y]</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">List3 = [Z]</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">List4 = [T]</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">X = Y = Z = T = term()</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\"></span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\"></span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\"></span></div>\n" + "</div></p>\n" + "<div class=\"REFBODY\"><p>\n" + " <p>Combine the elements of three lists of equal length into one\n" + " list. For each triple <span class=\"code\">X, Y, Z</span> of list elements from\n" + " the three lists, the element in the result list will be\n" + " <span class=\"code\">Combine(X, Y, Z)</span>.</p>\n" + " <p><span class=\"code\">zipwith3(fun(X, Y, Z) -> {X,Y,Z} end, List1, List2, List3)</span> is equivalent to <span class=\"code\">zip3(List1, List2, List3)</span>.</p>\n" + " <p>Examples:</p>\n" + " <div class=\"example\"><pre>\n" + "> <span class=\"bold_code\">lists:zipwith3(fun(X, Y, Z) -> X+Y+Z end, [1,2,3], [4,5,6], [7,8,9]).</span>\n" + "[12,15,18]\n" + "> <span class=\"bold_code\">lists:zipwith3(fun(X, Y, Z) -> [X,Y,Z] end, [a,b,c], [x,y,z], [1,2,3]).</span>\n" + "[[a,x,1],[b,y,2],[c,z,3]]</pre></div>\n" + " </p></div>\n" + " \n" + "</div>\n" + "</body></html>\n", "" + "-module(test).\n" + "test() ->\n" + " lists:zip<caret>with3(foo, bar, preved, medved).\n"); } public void testGenerateDocSdkModule() { doTestExternalDoc( "<html>\n" + ErlangSdkDocProviderBase.HTTP_STYLE + "<body>\n" + " <h3>MODULE</h3><div class=\"REFBODY\">lists</div>\n" + " <h3>MODULE SUMMARY</h3>\n" + "<div class=\"REFBODY\">List Processing Functions</div>\n" + " <h3>DESCRIPTION</h3>\n" + "<div class=\"REFBODY\"><p>\n" + " <p>This module contains functions for list processing.</p>\n" + "\n" + " <p>Unless otherwise stated, all functions assume that position\n" + " numbering starts at 1. That is, the first element of a list is at\n" + " position 1.</p>\n" + "\n" + " <p>Two terms <span class=\"code\">T1</span> and <span class=\"code\">T2</span> compare equal if\n" + " <span class=\"code\">T1 == T2</span> evaluates to <span class=\"code\">true</span>. They match\n" + " if <span class=\"code\">T1 =:= T2</span> evaluates to <span class=\"code\">true</span>.</p>\n" + "\n" + " <p>Whenever an <a name=\"ordering_function\"></a><strong>ordering function</strong>\n" + " <span class=\"code\">F</span> is expected as argument, it is assumed that the\n" + " following properties hold of <span class=\"code\">F</span> for all x, y and z:</p>\n" + " <ul>\n" + " <li>\n" + "<p>if x <span class=\"code\">F</span> y and y <span class=\"code\">F</span> x then x = y (<span class=\"code\">F</span>\n" + " is antisymmetric);</p>\n" + " </li>\n" + " <li>\n" + "<p>if x <span class=\"code\">F</span> y and y <span class=\"code\">F</span> z then x <span class=\"code\">F</span> z\n" + " (<span class=\"code\">F</span> is transitive);</p>\n" + " </li>\n" + " <li>\n" + "<p>x <span class=\"code\">F</span> y or y <span class=\"code\">F</span> x (<span class=\"code\">F</span> is total).</p>\n" + " </li>\n" + " </ul>\n" + " <p>An example of a typical ordering function is less than or equal\n" + " to, <span class=\"code\">=</2</span>.</p>\n" + "\n" + " </p></div>\n" + "</body></html>\n", "" + "-module(test).\n" + "test() ->\n" + " lis<caret>ts:foreach(foo, bar).\n"); } public void testGenerateDocSdkType() { doTestExternalDoc( "<html>\n" + ErlangSdkDocProviderBase.HTTP_STYLE + "<body>\n" + " <span class=\"bold_code\"><a name=\"type-file_info\">file_info()</a> = <br>    #file_info{size = undefined | integer() >= 0,<br>               type = undefined<br>                     | device<br>                     | directory<br>                     | other<br>                     | regular<br>                     | symlink,<br>               access = undefined<br>                       | read<br>                       | write<br>                       | read_write<br>                       | none,<br>               atime = undefined | <span class=\"bold_code\"><a href=\"psi_element://file#type-date_time\">file:date_time()</a></span> | integer(),<br>               mtime = undefined | <span class=\"bold_code\"><a href=\"psi_element://file#type-date_time\">file:date_time()</a></span> | integer(),<br>               ctime = undefined | <span class=\"bold_code\"><a href=\"psi_element://file#type-date_time\">file:date_time()</a></span> | integer(),<br>               mode = undefined | integer(),<br>               links = undefined | integer() >= 0,<br>               major_device = undefined | integer(),<br>               minor_device = undefined | integer(),<br>               inode = undefined | integer(),<br>               uid = undefined | integer(),<br>               gid = undefined | integer()}</span><br></p> <p>\n" + "</body></html>\n", "" + "-module(test).\n" + "-spec test(file:fil<caret>e_info()) -> ok." + "test(_FileInfo) ->\n" + " ok.\n"); } public void testGenerateDocSdkLastType() { doTestExternalDoc( "<html>\n" + ErlangSdkDocProviderBase.HTTP_STYLE + "<body>\n" + " <span class=\"bold_code\"><a name=\"type-sendfile_option\">sendfile_option()</a> = {chunk_size, integer() >= 0}</span><br></p> \n" + "\n" + "</body></html>\n", "" + "-module(test).\n" + "-spec test(file:send<caret>file_option()) -> ok." + "test(_SendFileOption) ->\n" + " ok.\n"); } public void testLinkConverterLocal() { // <a href="#write_$FUNC$-$ARITY$"> // <a href="#type-$TYPE$"> doTestExternalDoc( "<html>\n" + ErlangSdkDocProviderBase.HTTP_STYLE + "<body>\n" + " <p><a name=\"change_time-2\"></a><span class=\"bold_code\">change_time(Filename, Mtime) -> ok | {error, Reason}</span><br><div class=\"REFBODY\"><p>Types:</p>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Filename = <span class=\"bold_code\"><a href=\"psi_element://file#type-name\">name()</a></span></span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Mtime = <span class=\"bold_code\"><a href=\"psi_element://file#type-date_time\">date_time()</a></span></span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Reason = <span class=\"bold_code\"><a href=\"psi_element://file#type-posix\">posix()</a></span> | badarg</span></div>\n" + "</div></p>\n" + "<div class=\"REFBODY\"><p>\n" + " <p>Changes the modification and access times of a file. See\n" + " <span class=\"bold_code\"><a href=\"psi_element://file#write_file_info-2\">write_file_info/2</a></span>.</p>\n" + " </p></div>\n" + "</body></html>\n", "" + "-module(test).\n" + "test() ->\n" + " file:cha<caret>nge_time(foo, bar).\n"); } public void testLinkConverterErlRef() { // <a href="javascript:erlhref('$REL-PATH$','$APPLICATION$','$MODULE$.html#type-$TYPE$');"> // <a href="javascript:erlhref('$REL-PATH$','$APPLICATION$','$MODULE$.html');"> doTestExternalDoc( "<html>\n" + ErlangSdkDocProviderBase.HTTP_STYLE + "<body>\n" + " <p><a name=\"path_script-3\"></a><span class=\"bold_code\">path_script(Path, Filename, Bindings) -><br>               {ok, Value, FullName} | {error, Reason}</span><br><div class=\"REFBODY\"><p>Types:</p>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Path = [Dir :: <span class=\"bold_code\"><a href=\"psi_element://file#type-name\">name()</a></span>]</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Filename = <span class=\"bold_code\"><a href=\"psi_element://file#type-name\">name()</a></span></span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Bindings = <span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-binding_struct\">erl_eval:binding_struct()</a></span></span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Value = term()</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">FullName = <span class=\"bold_code\"><a href=\"psi_element://file#type-filename\">filename()</a></span></span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Reason = <span class=\"bold_code\"><a href=\"psi_element://file#type-posix\">posix()</a></span><br>       | badarg<br>       | terminated<br>       | system_limit<br>       | {Line :: integer(), Mod :: module(), Term :: term()}</span></div>\n" + "</div></p>\n" + "<div class=\"REFBODY\"><p>\n" + " <p>The same as <span class=\"code\">path_script/2</span> but the variable bindings\n" + " <span class=\"code\">Bindings</span> are used in the evaluation. See\n" + " <span class=\"bold_code\"><a href=\"psi_element://erl_eval\">erl_eval(3)</a></span> about\n" + " variable bindings.</p>\n" + " </p></div>\n" + "</body></html>\n", "" + "-module(test).\n" + "test() ->\n" + " file:pat<caret>h_script(1, 2, 3).\n"); } public void testLinkConverterErlRef2() { // <a href="javascript:erlhref('$REL-PATH$','$APPLICATION$','$MODULE$.html#$FUNCTION$-$ARITY$');"> doTestExternalDoc( "<html>\n" + ErlangSdkDocProviderBase.HTTP_STYLE + "<body>\n" + " <p><a name=\"read_line-1\"></a><span class=\"bold_code\">read_line(IoDevice) -> {ok, Data} | eof | {error, Reason}</span><br><div class=\"REFBODY\"><p>Types:</p>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">IoDevice = <span class=\"bold_code\"><a href=\"psi_element://file#type-io_device\">io_device()</a></span> | atom()</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Data = string() | binary()</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Reason = <span class=\"bold_code\"><a href=\"psi_element://file#type-posix\">posix()</a></span> | badarg | terminated</span></div>\n" + "</div></p>\n" + "<div class=\"REFBODY\"><p>\n" + " <p>Reads a line of bytes/characters from the file referenced by\n" + " <span class=\"code\">IoDevice</span>. Lines are defined to be delimited by the linefeed (LF, <span class=\"code\">\\n</span>) character, but any carriage return (CR, <span class=\"code\">\\r</span>) followed by a newline is also treated as a single LF character (the carriage return is silently ignored). The line is returned <strong>including</strong> the LF, but excluding any CR immediately followed by a LF. This behaviour is consistent with the behaviour of <span class=\"bold_code\"><a href=\"psi_element://io#get_line-2\">io:get_line/2</a></span>. If end of file is reached without any LF ending the last line, a line with no trailing LF is returned.</p>\n" + "\t <p>The function can be used on files opened in <span class=\"code\">raw</span> mode. It is however inefficient to use it on <span class=\"code\">raw</span> files if the file is not opened with the option <span class=\"code\">{read_ahead, Size}</span> specified, why combining <span class=\"code\">raw</span> and <span class=\"code\">{read_ahead, Size}</span> is highly recommended when opening a text file for raw line oriented reading.</p> \n" + "\t <p>If <span class=\"code\">encoding</span> is set to something else than <span class=\"code\">latin1</span>, the <span class=\"code\">read_line/1</span> call will fail if the data contains characters larger than 255, why the <span class=\"bold_code\"><a href=\"psi_element://io\">io(3)</a></span> module is to be preferred when reading such a file.</p> \n" + "\t <p>The function returns:</p>\n" + " <dl>\n" + " <dt><strong><span class=\"code\">{ok, Data}</span></strong></dt>\n" + " <dd>\n" + "\t <p>One line from the file is returned, including the trailing LF, but with CRLF sequences replaced by a single LF (see above).</p>\n" + " <p>If the file was opened in binary mode, the read bytes are\n" + " returned in a binary, otherwise in a list.</p>\n" + " </dd>\n" + " <dt><strong><span class=\"code\">eof</span></strong></dt>\n" + " <dd>\n" + " <p>Returned if end of file was reached\n" + " before anything at all could be read.</p>\n" + " </dd>\n" + " <dt><strong><span class=\"code\">{error, Reason}</span></strong></dt>\n" + " <dd>\n" + " <p>An error occurred.</p>\n" + " </dd>\n" + " </dl>\n" + " <p>Typical error reasons:</p>\n" + " <dl>\n" + " <dt><strong><span class=\"code\">ebadf</span></strong></dt>\n" + " <dd>\n" + " <p>The file is not opened for reading.</p>\n" + " </dd>\n" + " <dt><strong><span class=\"code\">{no_translation, unicode, latin1}</span></strong></dt>\n" + " <dd>\n" + " <p>The file is was opened with another <span class=\"code\">encoding</span> than <span class=\"code\">latin1</span> and the data on the file can not be translated to the byte-oriented data that this function returns.</p>\n" + " </dd>\n" + " </dl>\n" + " </p></div>\n" + "</body></html>\n", "" + "-module(test).\n" + "test() ->\n" + " file:rea<caret>d_line(1).\n"); } public void testLinkConverterInModule() { // <a href="$MODULE$.html#$FUNCTION$-$ARITY$"> // <a href="$MODULE$.html#type-$TYPE$"> doTestExternalDoc( "<html>\n" + ErlangSdkDocProviderBase.HTTP_STYLE + "<body>\n" + " <p><a name=\"sendfile-5\"></a><span class=\"bold_code\">sendfile(RawFile, Socket, Offset, Bytes, Opts) -><br>            {ok, integer() >= 0} |<br>            {error, <span class=\"bold_code\"><a href=\"psi_element://inet#type-posix\">inet:posix()</a></span> | closed | badarg | not_owner}</span><br><div class=\"REFBODY\"><p>Types:</p>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">RawFile = <span class=\"bold_code\"><a href=\"psi_element://file#type-fd\">file:fd()</a></span></span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Socket = <span class=\"bold_code\"><a href=\"psi_element://inet#type-socket\">inet:socket()</a></span></span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Offset = Bytes = integer() >= 0</span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\"></span></div>\n" + "<div class=\"REFTYPES\"><span class=\"bold_code\">Opts = [<span class=\"bold_code\"><a href=\"psi_element://file#type-sendfile_option\">sendfile_option()</a></span>]</span></div>\n" + "</div></p>\n" + "<div class=\"REFBODY\"><p>\n" + " <p>Sends <span class=\"code\">Bytes</span> from the file\n" + " referenced by <span class=\"code\">RawFile</span> beginning at <span class=\"code\">Offset</span> to\n" + " <span class=\"code\">Socket</span>.\n" + " Returns <span class=\"code\">{ok, BytesSent}</span> if successful,\n" + " otherwise <span class=\"code\">{error, Reason}</span>. If <span class=\"code\">Bytes</span> is set to\n" + "\t0 all data after the given <span class=\"code\">Offset</span> is sent.</p>\n" + "\t<p>The file used must be opened using the raw flag, and the process\n" + "\tcalling sendfile must be the controlling process of the socket.\n" + "\tSee <span class=\"bold_code\"><a href=\"psi_element://gen_tcp#controlling_process-2\">gen_tcp:controlling_process/2</a></span></p>\n" + "\t<p>If the OS used does not support sendfile, an Erlang fallback\n" + "\tusing file:read and gen_tcp:send is used.</p>\n" + "\t<p>The option list can contain the following options:\n" + "\t<dl>\n" + " <dt><strong><span class=\"code\">chunk_size</span></strong></dt>\n" + " <dd>The chunk size used by the erlang fallback to send\n" + "\t data. If using the fallback, this should be set to a value\n" + "\t which comfortably fits in the systems memory. Default is 20 MB.</dd>\n" + "\t</dl>\n" + "\t</p>\n" + "\t<p>On operating systems with thread support, it is recommended to use\n" + "\tasync threads. See the command line flag\n" + "\t<span class=\"code\">+A</span> in <span class=\"bold_code\"><a href=\"psi_element://erl\">erl(1)</a></span>. If it is not\n" + "\tpossible to use async threads for sendfile, it is recommended to use\n" + "\ta relatively small value for the send buffer on the socket. Otherwise\n" + "\tthe Erlang VM might loose some of its soft realtime guarantees.\n" + "\tWhich size to use depends on the OS/hardware and the requirements\n" + "\tof the application.</p>\n" + " </p></div>\n" + "</body></html>\n", "" + "-module(test).\n" + "test() ->\n" + " file:sen<caret>dfile(1,2,3,4,5).\n"); } public void testLinkConverterWithLineBreak() { doTestExternalDoc( "<html>\n" + ErlangSdkDocProviderBase.HTTP_STYLE + "<body>\n" + " <h3>MODULE</h3><div class=\"REFBODY\">erl_eval</div>\n" + " <h3>MODULE SUMMARY</h3>\n" + "<div class=\"REFBODY\">The Erlang Meta Interpreter</div>\n" + " <h3>DESCRIPTION</h3>\n" + "<div class=\"REFBODY\"><p>\n" + " <p>This module provides an interpreter for Erlang expressions. The\n" + " expressions are in the abstract syntax as returned by\n" + " <span class=\"bold_code\"><a href=\"psi_element://erl_parse\"><span class=\"code\">erl_parse</span></a></span>,\n" + // Link to module `io` is multi-line " the Erlang parser, or <span class=\"bold_code\"><a href=\"psi_element://io\">\n" + " <span class=\"code\">io</span></a></span>.</p>\n" + " </p></div>\n" + " <h3>DATA TYPES</h3>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-bindings\">bindings()</a> = [{<span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-name\">name()</a></span>, <span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-value\">value()</a></span>}]</span><br></p>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-binding_struct\">binding_struct()</a> = <span class=\"bold_code\"><a href=\"psi_element://orddict#type-orddict\">orddict:orddict()</a></span></span><br></p>\n" + "<div class=\"REFBODY\"><p><p>A binding structure.</p></p></div>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-expression\">expression()</a> = <span class=\"bold_code\"><a href=\"psi_element://erl_parse#type-abstract_expr\">erl_parse:abstract_expr()</a></span></span><br></p>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-expressions\">expressions()</a> = [<span class=\"bold_code\"><a href=\"psi_element://erl_parse#type-abstract_expr\">erl_parse:abstract_expr()</a></span>]</span><br></p>\n" + "<div class=\"REFBODY\"><p><p>As returned by <span class=\"bold_code\"><a href=\"psi_element://erl_parse#parse_exprs-1\">\n" + " <span class=\"code\">erl_parse:parse_exprs/1</span></a></span> or\n" + " <span class=\"bold_code\"><a href=\"psi_element://io#parse_erl_exprs-2\">\n" + " <span class=\"code\">io:parse_erl_exprs/2</span></a></span>.</p></p></div>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-expression_list\">expression_list()</a> = [<span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-expression\">expression()</a></span>]</span><br></p>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-func_spec\">func_spec()</a> = {Module :: module(), Function :: atom()}<br>            | function()</span><br></p>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-lfun_eval_handler\">lfun_eval_handler()</a> = <br>    fun((Name :: atom(),<br>         Arguments :: <span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-expression_list\">expression_list()</a></span>,<br>         Bindings :: <span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-binding_struct\">binding_struct()</a></span>) -><br>            {value,<br>             Value :: <span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-value\">value()</a></span>,<br>             NewBindings :: <span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-binding_struct\">binding_struct()</a></span>})</span><br></p>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-lfun_value_handler\">lfun_value_handler()</a> = <br>    fun((Name :: atom(), Arguments :: [term()]) -><br>            Value :: <span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-value\">value()</a></span>)</span><br></p>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-local_function_handler\">local_function_handler()</a> = {value, <span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-lfun_value_handler\">lfun_value_handler()</a></span>}<br>                         | {eval, <span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-lfun_eval_handler\">lfun_eval_handler()</a></span>}<br>                         | none</span><br></p>\n" + "<div class=\"REFBODY\"><p><p>Further described\n" + " <span class=\"bold_code\"><a href=\"psi_element://erl_eval#local_function_handler\">below.</a></span></p>\n" + " </p></div>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-name\">name()</a> = term()</span><br></p>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-nlfun_handler\">nlfun_handler()</a> = <br>    fun((FuncSpec :: <span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-func_spec\">func_spec()</a></span>, Arguments :: [term()]) -> term())</span><br></p>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-non_local_function_handler\">non_local_function_handler()</a> = {value, <span class=\"bold_code\"><a href=\"psi_element://erl_eval#type-nlfun_handler\">nlfun_handler()</a></span>} | none</span><br></p>\n" + "<div class=\"REFBODY\"><p><p>Further described\n" + " <span class=\"bold_code\"><a href=\"psi_element://erl_eval#non_local_function_handler\">below.</a></span></p>\n" + " </p></div>\n" + " <p>\n" + " <span class=\"bold_code\"><a name=\"type-value\">value()</a> = term()</span><br></p>\n" + " \n" + "</body></html>\n", "" + "-module(test).\n" + "test() ->\n" + " erl<caret>_eval:expr(1, 2).\n"); } public void testResolveLinkModule() { PsiElement psiElement = myErlangDocProvider.getDocumentationElementForLink(getPsiManager(), "file", null); assertTrue("Not a module: " + psiElement, psiElement instanceof ErlangModule); } public void testResolveLinkFunction() { PsiElement psiElement = myErlangDocProvider.getDocumentationElementForLink(getPsiManager(), "file#read_line-1", null); assertTrue("Not a function: " + psiElement, psiElement instanceof ErlangFunction); } public void testResolveLinkType() { PsiElement psiElement = myErlangDocProvider.getDocumentationElementForLink(getPsiManager(), "file#type-filename", null); assertTrue("Not a type: " + psiElement, psiElement instanceof ErlangTypeDefinition); } @NotNull @Override protected LightProjectDescriptor getProjectDescriptor() { return new DefaultLightProjectDescriptor() { @Override public Sdk getSdk() { Sdk mockSdk = ErlangSdkType.createMockSdk("testData/mockSdk-R15B02/", ErlangSdkRelease.V_R15B02); // Set local SDK documentation path SdkModificator sdkModificator = mockSdk.getSdkModificator(); VirtualFile localDocDir = LocalFileSystem.getInstance().findFileByPath("testData/mockSdk-R15B02/"); sdkModificator.addRoot(localDocDir, JavadocOrderRootType.getInstance()); sdkModificator.commitChanges(); return mockSdk; } }; } private void doTestGetUrls(@NotNull String expected, @NotNull String text) { PsiElement element = resolveElementAtCaret(text); assertEquals(expected, myErlangDocProvider.getUrlFor(element, null).get(0)); } private void doTestExternalDoc(@NotNull String expected, @NotNull String text) { PsiElement element = resolveElementAtCaret(text); List<String> urls = myErlangDocProvider.getUrlFor(element, null); assertEquals(expected, myErlangDocProvider.fetchExternalDocumentation(getProject(), element, urls)); } @NotNull private PsiElement resolveElementAtCaret(@NotNull String text) { myFixture.configureByText("test.erl", text); int caretPosition = myFixture.getEditor().getCaretModel().getOffset(); PsiReference psiReference = myFixture.getFile().findReferenceAt(caretPosition); PsiElement resolve = psiReference.resolve(); assertNotNull(resolve); return resolve; } }