diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2fefa1a..c54a188 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1,412 +1,416 @@ 2, 'class' => array( 'AASTNode' => 'parser/aast/api/AASTNode.php', 'AASTNodeList' => 'parser/aast/api/AASTNodeList.php', 'AASTToken' => 'parser/aast/api/AASTToken.php', 'AASTTree' => 'parser/aast/api/AASTTree.php', 'AbstractDirectedGraph' => 'utils/AbstractDirectedGraph.php', 'AbstractDirectedGraphTestCase' => 'utils/__tests__/AbstractDirectedGraphTestCase.php', 'AphrontDatabaseConnection' => 'aphront/storage/connection/AphrontDatabaseConnection.php', 'AphrontDatabaseTransactionState' => 'aphront/storage/connection/AphrontDatabaseTransactionState.php', 'AphrontIsolatedDatabaseConnection' => 'aphront/storage/connection/AphrontIsolatedDatabaseConnection.php', 'AphrontMySQLDatabaseConnection' => 'aphront/storage/connection/mysql/AphrontMySQLDatabaseConnection.php', 'AphrontMySQLDatabaseConnectionBase' => 'aphront/storage/connection/mysql/AphrontMySQLDatabaseConnectionBase.php', 'AphrontMySQLiDatabaseConnection' => 'aphront/storage/connection/mysql/AphrontMySQLiDatabaseConnection.php', 'AphrontQueryAccessDeniedException' => 'aphront/storage/exception/AphrontQueryAccessDeniedException.php', 'AphrontQueryConnectionException' => 'aphront/storage/exception/AphrontQueryConnectionException.php', 'AphrontQueryConnectionLostException' => 'aphront/storage/exception/AphrontQueryConnectionLostException.php', 'AphrontQueryCountException' => 'aphront/storage/exception/AphrontQueryCountException.php', 'AphrontQueryDeadlockException' => 'aphront/storage/exception/AphrontQueryDeadlockException.php', 'AphrontQueryDuplicateKeyException' => 'aphront/storage/exception/AphrontQueryDuplicateKeyException.php', 'AphrontQueryException' => 'aphront/storage/exception/AphrontQueryException.php', 'AphrontQueryNotSupportedException' => 'aphront/storage/exception/AphrontQueryNotSupportedException.php', 'AphrontQueryObjectMissingException' => 'aphront/storage/exception/AphrontQueryObjectMissingException.php', 'AphrontQueryParameterException' => 'aphront/storage/exception/AphrontQueryParameterException.php', 'AphrontQueryRecoverableException' => 'aphront/storage/exception/AphrontQueryRecoverableException.php', 'AphrontQuerySchemaException' => 'aphront/storage/exception/AphrontQuerySchemaException.php', 'AphrontScopedUnguardedWriteCapability' => 'aphront/writeguard/AphrontScopedUnguardedWriteCapability.php', 'AphrontWriteGuard' => 'aphront/writeguard/AphrontWriteGuard.php', 'BaseHTTPFuture' => 'future/http/BaseHTTPFuture.php', 'CommandException' => 'future/CommandException.php', 'ConduitClient' => 'conduit/ConduitClient.php', 'ConduitClientException' => 'conduit/ConduitClientException.php', 'ConduitFuture' => 'conduit/ConduitFuture.php', 'ExecFuture' => 'future/ExecFuture.php', 'ExecFutureTestCase' => 'future/__tests__/ExecFutureTestCase.php', 'FileFinder' => 'filesystem/FileFinder.php', 'FileList' => 'filesystem/FileList.php', 'Filesystem' => 'filesystem/Filesystem.php', 'FilesystemException' => 'filesystem/FilesystemException.php', 'Future' => 'future/Future.php', 'FutureIterator' => 'future/FutureIterator.php', 'FutureIteratorTestCase' => 'future/__tests__/FutureIteratorTestCase.php', 'FutureProxy' => 'future/FutureProxy.php', 'HTTPFuture' => 'future/http/HTTPFuture.php', 'HTTPFutureResponseStatus' => 'future/http/status/HTTPFutureResponseStatus.php', 'HTTPFutureResponseStatusCURL' => 'future/http/status/HTTPFutureResponseStatusCURL.php', 'HTTPFutureResponseStatusHTTP' => 'future/http/status/HTTPFutureResponseStatusHTTP.php', 'HTTPFutureResponseStatusParse' => 'future/http/status/HTTPFutureResponseStatusParse.php', 'HTTPFutureResponseStatusTransport' => 'future/http/status/HTTPFutureResponseStatusTransport.php', 'HTTPSFuture' => 'future/http/HTTPSFuture.php', 'ImmediateFuture' => 'future/ImmediateFuture.php', 'LinesOfALarge' => 'filesystem/linesofalarge/LinesOfALarge.php', 'LinesOfALargeExecFuture' => 'filesystem/linesofalarge/LinesOfALargeExecFuture.php', 'LinesOfALargeExecFutureTestCase' => 'filesystem/linesofalarge/__tests__/LinesOfALargeExecFutureTestCase.php', 'LinesOfALargeFile' => 'filesystem/linesofalarge/LinesOfALargeFile.php', 'LinesOfALargeFileTestCase' => 'filesystem/linesofalarge/__tests__/LinesOfALargeFileTestCase.php', 'MFilterTestHelper' => 'utils/__tests__/MFilterTestHelper.php', 'PhutilAWSEC2Future' => 'future/aws/PhutilAWSEC2Future.php', 'PhutilAWSException' => 'future/aws/PhutilAWSException.php', 'PhutilAWSFuture' => 'future/aws/PhutilAWSFuture.php', 'PhutilAggregateException' => 'error/PhutilAggregateException.php', 'PhutilArgumentParser' => 'parser/argument/PhutilArgumentParser.php', 'PhutilArgumentParserException' => 'parser/argument/exception/PhutilArgumentParserException.php', 'PhutilArgumentParserTestCase' => 'parser/argument/__tests__/PhutilArgumentParserTestCase.php', 'PhutilArgumentSpecification' => 'parser/argument/PhutilArgumentSpecification.php', 'PhutilArgumentSpecificationException' => 'parser/argument/exception/PhutilArgumentSpecificationException.php', 'PhutilArgumentSpecificationTestCase' => 'parser/argument/__tests__/PhutilArgumentSpecificationTestCase.php', 'PhutilArgumentUsageException' => 'parser/argument/exception/PhutilArgumentUsageException.php', 'PhutilArgumentWorkflow' => 'parser/argument/workflow/PhutilArgumentWorkflow.php', 'PhutilBufferedIterator' => 'utils/PhutilBufferedIterator.php', 'PhutilBufferedIteratorExample' => 'utils/PhutilBufferedIteratorExample.php', 'PhutilBufferedIteratorTestCase' => 'utils/__tests__/PhutilBufferedIteratorTestCase.php', 'PhutilChannel' => 'channel/PhutilChannel.php', 'PhutilChannelChannel' => 'channel/PhutilChannelChannel.php', 'PhutilConsole' => 'console/PhutilConsole.php', 'PhutilConsoleFormatter' => 'console/PhutilConsoleFormatter.php', 'PhutilConsoleMessage' => 'console/PhutilConsoleMessage.php', 'PhutilConsoleServer' => 'console/PhutilConsoleServer.php', 'PhutilConsoleServerChannel' => 'console/PhutilConsoleServerChannel.php', 'PhutilConsoleStdinNotInteractiveException' => 'console/PhutilConsoleStdinNotInteractiveException.php', 'PhutilConsoleSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilConsoleSyntaxHighlighter.php', 'PhutilConsoleWrapTestCase' => 'console/__tests__/PhutilConsoleWrapTestCase.php', 'PhutilDaemon' => 'daemon/PhutilDaemon.php', 'PhutilDaemonOverseer' => 'daemon/PhutilDaemonOverseer.php', 'PhutilDefaultSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilDefaultSyntaxHighlighter.php', 'PhutilDefaultSyntaxHighlighterEngine' => 'markup/syntax/engine/PhutilDefaultSyntaxHighlighterEngine.php', 'PhutilDefaultSyntaxHighlighterEnginePygmentsFuture' => 'markup/syntax/highlighter/pygments/PhutilDefaultSyntaxHighlighterEnginePygmentsFuture.php', 'PhutilDefaultSyntaxHighlighterEngineTestCase' => 'markup/syntax/engine/__tests__/PhutilDefaultSyntaxHighlighterEngineTestCase.php', 'PhutilDeferredLog' => 'filesystem/PhutilDeferredLog.php', 'PhutilDeferredLogTestCase' => 'filesystem/__tests__/PhutilDeferredLogTestCase.php', 'PhutilDivinerSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilDivinerSyntaxHighlighter.php', 'PhutilDocblockParser' => 'parser/PhutilDocblockParser.php', 'PhutilDocblockParserTestCase' => 'parser/__tests__/PhutilDocblockParserTestCase.php', 'PhutilEmailAddress' => 'parser/PhutilEmailAddress.php', 'PhutilEmailAddressTestCase' => 'parser/__tests__/PhutilEmailAddressTestCase.php', 'PhutilErrorHandler' => 'error/PhutilErrorHandler.php', 'PhutilEvent' => 'events/PhutilEvent.php', 'PhutilEventConstants' => 'events/constant/PhutilEventConstants.php', 'PhutilEventEngine' => 'events/PhutilEventEngine.php', 'PhutilEventListener' => 'events/PhutilEventListener.php', 'PhutilEventType' => 'events/constant/PhutilEventType.php', 'PhutilExcessiveServiceCallsDaemon' => 'daemon/torture/PhutilExcessiveServiceCallsDaemon.php', 'PhutilExecChannel' => 'channel/PhutilExecChannel.php', 'PhutilFatalDaemon' => 'daemon/torture/PhutilFatalDaemon.php', 'PhutilFileLock' => 'filesystem/PhutilFileLock.php', 'PhutilFileLockTestCase' => 'filesystem/__tests__/PhutilFileLockTestCase.php', 'PhutilFileTree' => 'filesystem/PhutilFileTree.php', 'PhutilGitURI' => 'parser/PhutilGitURI.php', 'PhutilGitURITestCase' => 'parser/__tests__/PhutilGitURITestCase.php', 'PhutilHangForeverDaemon' => 'daemon/torture/PhutilHangForeverDaemon.php', 'PhutilHelpArgumentWorkflow' => 'parser/argument/workflow/PhutilHelpArgumentWorkflow.php', 'PhutilInteractiveEditor' => 'console/PhutilInteractiveEditor.php', 'PhutilJSON' => 'parser/PhutilJSON.php', 'PhutilJSONProtocolChannel' => 'channel/PhutilJSONProtocolChannel.php', 'PhutilJSONProtocolChannelTestCase' => 'channel/__tests__/PhutilJSONProtocolChannelTestCase.php', 'PhutilJSONTestCase' => 'parser/__tests__/PhutilJSONTestCase.php', 'PhutilLanguageGuesser' => 'parser/PhutilLanguageGuesser.php', 'PhutilLanguageGuesserTestCase' => 'parser/__tests__/PhutilLanguageGuesserTestCase.php', 'PhutilLexer' => 'lexer/PhutilLexer.php', + 'PhutilLexerSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilLexerSyntaxHighlighter.php', 'PhutilLock' => 'filesystem/PhutilLock.php', 'PhutilLockException' => 'filesystem/PhutilLockException.php', 'PhutilMarkupEngine' => 'markup/PhutilMarkupEngine.php', 'PhutilMarkupTestCase' => 'markup/__tests__/PhutilMarkupTestCase.php', 'PhutilMissingSymbolException' => 'symbols/exception/PhutilMissingSymbolException.php', 'PhutilNiceDaemon' => 'daemon/torture/PhutilNiceDaemon.php', 'PhutilOpaqueEnvelope' => 'error/PhutilOpaqueEnvelope.php', 'PhutilOpaqueEnvelopeKey' => 'error/PhutilOpaqueEnvelopeKey.php', 'PhutilOpaqueEnvelopeTestCase' => 'error/__tests__/PhutilOpaqueEnvelopeTestCase.php', 'PhutilPHPFragmentLexer' => 'lexer/PhutilPHPFragmentLexer.php', + 'PhutilPHPFragmentLexerHighlighterTestCase' => 'markup/syntax/highlighter/__tests__/PhutilPHPFragmentLexerHighlighterTestCase.php', 'PhutilPHPFragmentLexerTestCase' => 'lexer/__tests__/PhutilPHPFragmentLexerTestCase.php', 'PhutilPHPObjectProtocolChannel' => 'channel/PhutilPHPObjectProtocolChannel.php', 'PhutilPHPObjectProtocolChannelTestCase' => 'channel/__tests__/PhutilPHPObjectProtocolChannelTestCase.php', 'PhutilPHTTestCase' => 'internationalization/__tests__/PhutilPHTTestCase.php', 'PhutilPerson' => 'internationalization/PhutilPerson.php', 'PhutilPersonTest' => 'internationalization/__tests__/PhutilPersonTest.php', 'PhutilProcessGroupDaemon' => 'daemon/torture/PhutilProcessGroupDaemon.php', 'PhutilProtocolChannel' => 'channel/PhutilProtocolChannel.php', 'PhutilPygmentsSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilPygmentsSyntaxHighlighter.php', 'PhutilRainbowSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilRainbowSyntaxHighlighter.php', 'PhutilReadableSerializer' => 'readableserializer/PhutilReadableSerializer.php', 'PhutilRemarkupBlockStorage' => 'markup/engine/remarkup/PhutilRemarkupBlockStorage.php', 'PhutilRemarkupEngine' => 'markup/engine/PhutilRemarkupEngine.php', 'PhutilRemarkupEngineBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineBlockRule.php', 'PhutilRemarkupEngineRemarkupCodeBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupCodeBlockRule.php', 'PhutilRemarkupEngineRemarkupDefaultBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupDefaultBlockRule.php', 'PhutilRemarkupEngineRemarkupHeaderBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupHeaderBlockRule.php', 'PhutilRemarkupEngineRemarkupInlineBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupInlineBlockRule.php', 'PhutilRemarkupEngineRemarkupListBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupListBlockRule.php', 'PhutilRemarkupEngineRemarkupLiteralBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupLiteralBlockRule.php', 'PhutilRemarkupEngineRemarkupNoteBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupNoteBlockRule.php', 'PhutilRemarkupEngineRemarkupQuotesBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupQuotesBlockRule.php', 'PhutilRemarkupEngineRemarkupTableBlockRule' => 'markup/engine/remarkup/blockrule/PhutilRemarkupEngineRemarkupTableBlockRule.php', 'PhutilRemarkupEngineTestCase' => 'markup/engine/__tests__/PhutilRemarkupEngineTestCase.php', 'PhutilRemarkupRule' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRule.php', 'PhutilRemarkupRuleBold' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleBold.php', 'PhutilRemarkupRuleDel' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleDel.php', 'PhutilRemarkupRuleDocumentLink' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleDocumentLink.php', 'PhutilRemarkupRuleEscapeHTML' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleEscapeHTML.php', 'PhutilRemarkupRuleEscapeRemarkup' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleEscapeRemarkup.php', 'PhutilRemarkupRuleHyperlink' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleHyperlink.php', 'PhutilRemarkupRuleItalic' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleItalic.php', 'PhutilRemarkupRuleLinebreaks' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleLinebreaks.php', 'PhutilRemarkupRuleMonospace' => 'markup/engine/remarkup/markuprule/PhutilRemarkupRuleMonospace.php', 'PhutilSaturateStdoutDaemon' => 'daemon/torture/PhutilSaturateStdoutDaemon.php', 'PhutilServiceProfiler' => 'serviceprofiler/PhutilServiceProfiler.php', 'PhutilSimpleOptions' => 'parser/PhutilSimpleOptions.php', 'PhutilSimpleOptionsTestCase' => 'parser/__tests__/PhutilSimpleOptionsTestCase.php', 'PhutilSocketChannel' => 'channel/PhutilSocketChannel.php', 'PhutilSprite' => 'sprites/PhutilSprite.php', 'PhutilSpriteSheet' => 'sprites/PhutilSpriteSheet.php', 'PhutilSymbolLoader' => 'symbols/PhutilSymbolLoader.php', 'PhutilSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilSyntaxHighlighter.php', 'PhutilSyntaxHighlighterEngine' => 'markup/syntax/engine/PhutilSyntaxHighlighterEngine.php', 'PhutilSyntaxHighlighterException' => 'markup/syntax/highlighter/PhutilSyntaxHighlighterException.php', 'PhutilTortureTestDaemon' => 'daemon/torture/PhutilTortureTestDaemon.php', 'PhutilTranslator' => 'internationalization/PhutilTranslator.php', 'PhutilTranslatorTestCase' => 'internationalization/__tests__/PhutilTranslatorTestCase.php', 'PhutilURI' => 'parser/PhutilURI.php', 'PhutilURITestCase' => 'parser/__tests__/PhutilURITestCase.php', 'PhutilUTF8TestCase' => 'utils/__tests__/PhutilUTF8TestCase.php', 'PhutilUtilsTestCase' => 'utils/__tests__/PhutilUtilsTestCase.php', 'PhutilXHPASTSyntaxHighlighter' => 'markup/syntax/highlighter/PhutilXHPASTSyntaxHighlighter.php', 'PhutilXHPASTSyntaxHighlighterTestCase' => 'markup/syntax/highlighter/__tests__/PhutilXHPASTSyntaxHighlighterTestCase.php', 'TempFile' => 'filesystem/TempFile.php', 'TestAbstractDirectedGraph' => 'utils/__tests__/TestAbstractDirectedGraph.php', 'XHPASTNode' => 'parser/xhpast/api/XHPASTNode.php', 'XHPASTSyntaxErrorException' => 'parser/xhpast/api/XHPASTSyntaxErrorException.php', 'XHPASTToken' => 'parser/xhpast/api/XHPASTToken.php', 'XHPASTTree' => 'parser/xhpast/api/XHPASTTree.php', 'XHPASTTreeTestCase' => 'parser/xhpast/api/__tests__/XHPASTTreeTestCase.php', ), 'function' => array( 'Futures' => 'future/functions.php', '_qsprintf_check_scalar_type' => 'xsprintf/qsprintf.php', '_qsprintf_check_type' => 'xsprintf/qsprintf.php', 'array_mergev' => 'utils/utils.php', 'array_select_keys' => 'utils/utils.php', 'assert_instances_of' => 'utils/utils.php', 'coalesce' => 'utils/utils.php', 'csprintf' => 'xsprintf/csprintf.php', 'exec_manual' => 'future/execx.php', 'execx' => 'future/execx.php', 'head' => 'utils/utils.php', 'head_key' => 'utils/utils.php', 'hsprintf' => 'markup/render.php', 'id' => 'utils/utils.php', 'idx' => 'utils/utils.php', 'ifilter' => 'utils/utils.php', 'igroup' => 'utils/utils.php', 'ipull' => 'utils/utils.php', 'isort' => 'utils/utils.php', 'jsprintf' => 'xsprintf/jsprintf.php', 'last' => 'utils/utils.php', 'last_key' => 'utils/utils.php', 'ldap_sprintf' => 'xsprintf/ldapsprintf.php', 'mfilter' => 'utils/utils.php', 'mgroup' => 'utils/utils.php', 'mpull' => 'utils/utils.php', 'msort' => 'utils/utils.php', 'newv' => 'utils/utils.php', 'nonempty' => 'utils/utils.php', 'phlog' => 'error/phlog.php', 'pht' => 'internationalization/pht.php', 'phutil_console_confirm' => 'console/format.php', 'phutil_console_format' => 'console/format.php', 'phutil_console_prompt' => 'console/format.php', 'phutil_console_require_tty' => 'console/format.php', 'phutil_console_wrap' => 'console/format.php', 'phutil_deprecated' => 'moduleutils/moduleutils.php', 'phutil_error_listener_example' => 'error/phlog.php', 'phutil_escape_html' => 'markup/render.php', 'phutil_escape_uri' => 'markup/render.php', 'phutil_escape_uri_path_component' => 'markup/render.php', 'phutil_get_library_name_for_root' => 'moduleutils/moduleutils.php', 'phutil_get_library_root' => 'moduleutils/moduleutils.php', 'phutil_get_library_root_for_path' => 'moduleutils/moduleutils.php', 'phutil_is_utf8' => 'utils/utf8.php', 'phutil_passthru' => 'future/execx.php', 'phutil_render_tag' => 'markup/render.php', 'phutil_unescape_uri_path_component' => 'markup/render.php', 'phutil_utf8_convert' => 'utils/utf8.php', 'phutil_utf8_hard_wrap_html' => 'utils/utf8.php', 'phutil_utf8_shorten' => 'utils/utf8.php', 'phutil_utf8_strlen' => 'utils/utf8.php', 'phutil_utf8ize' => 'utils/utf8.php', 'phutil_utf8v' => 'utils/utf8.php', 'qsprintf' => 'xsprintf/qsprintf.php', 'queryfx' => 'xsprintf/queryfx.php', 'queryfx_all' => 'xsprintf/queryfx.php', 'queryfx_one' => 'xsprintf/queryfx.php', 'vcsprintf' => 'xsprintf/csprintf.php', 'vjsprintf' => 'xsprintf/jsprintf.php', 'vqsprintf' => 'xsprintf/qsprintf.php', 'vqueryfx' => 'xsprintf/queryfx.php', 'vqueryfx_all' => 'xsprintf/queryfx.php', 'xhp_parser_node_constants' => 'parser/xhpast/parser_nodes.php', 'xhpast_get_binary_path' => 'parser/xhpast/bin/xhpast_parse.php', 'xhpast_get_build_instructions' => 'parser/xhpast/bin/xhpast_parse.php', 'xhpast_get_parser_future' => 'parser/xhpast/bin/xhpast_parse.php', 'xhpast_is_available' => 'parser/xhpast/bin/xhpast_parse.php', 'xhpast_parser_token_constants' => 'parser/xhpast/parser_tokens.php', 'xsprintf' => 'xsprintf/xsprintf.php', 'xsprintf_callback_example' => 'xsprintf/xsprintf.php', 'xsprintf_command' => 'xsprintf/csprintf.php', 'xsprintf_javascript' => 'xsprintf/jsprintf.php', 'xsprintf_ldap' => 'xsprintf/ldapsprintf.php', 'xsprintf_query' => 'xsprintf/qsprintf.php', ), 'xmap' => array( 'AASTNodeList' => array( 0 => 'Iterator', 1 => 'Countable', ), 'AbstractDirectedGraphTestCase' => 'ArcanistPhutilTestCase', 'AphrontIsolatedDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontMySQLDatabaseConnection' => 'AphrontMySQLDatabaseConnectionBase', 'AphrontMySQLDatabaseConnectionBase' => 'AphrontDatabaseConnection', 'AphrontMySQLiDatabaseConnection' => 'AphrontMySQLDatabaseConnectionBase', 'AphrontQueryAccessDeniedException' => 'AphrontQueryRecoverableException', 'AphrontQueryConnectionException' => 'AphrontQueryException', 'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException', 'AphrontQueryCountException' => 'AphrontQueryException', 'AphrontQueryDeadlockException' => 'AphrontQueryRecoverableException', 'AphrontQueryDuplicateKeyException' => 'AphrontQueryException', 'AphrontQueryException' => 'Exception', 'AphrontQueryNotSupportedException' => 'AphrontQueryException', 'AphrontQueryObjectMissingException' => 'AphrontQueryException', 'AphrontQueryParameterException' => 'AphrontQueryException', 'AphrontQueryRecoverableException' => 'AphrontQueryException', 'AphrontQuerySchemaException' => 'AphrontQueryException', 'BaseHTTPFuture' => 'Future', 'CommandException' => 'Exception', 'ConduitClientException' => 'Exception', 'ConduitFuture' => 'FutureProxy', 'ExecFuture' => 'Future', 'ExecFutureTestCase' => 'ArcanistPhutilTestCase', 'FilesystemException' => 'Exception', 'FutureIterator' => 'Iterator', 'FutureIteratorTestCase' => 'ArcanistPhutilTestCase', 'FutureProxy' => 'Future', 'HTTPFuture' => 'BaseHTTPFuture', 'HTTPFutureResponseStatus' => 'Exception', 'HTTPFutureResponseStatusCURL' => 'HTTPFutureResponseStatus', 'HTTPFutureResponseStatusHTTP' => 'HTTPFutureResponseStatus', 'HTTPFutureResponseStatusParse' => 'HTTPFutureResponseStatus', 'HTTPFutureResponseStatusTransport' => 'HTTPFutureResponseStatus', 'HTTPSFuture' => 'BaseHTTPFuture', 'ImmediateFuture' => 'Future', 'LinesOfALarge' => 'Iterator', 'LinesOfALargeExecFuture' => 'LinesOfALarge', 'LinesOfALargeExecFutureTestCase' => 'ArcanistPhutilTestCase', 'LinesOfALargeFile' => 'LinesOfALarge', 'LinesOfALargeFileTestCase' => 'ArcanistPhutilTestCase', 'PhutilAWSEC2Future' => 'PhutilAWSFuture', 'PhutilAWSException' => 'Exception', 'PhutilAWSFuture' => 'FutureProxy', 'PhutilAggregateException' => 'Exception', 'PhutilArgumentParserException' => 'Exception', 'PhutilArgumentParserTestCase' => 'ArcanistPhutilTestCase', 'PhutilArgumentSpecificationException' => 'PhutilArgumentParserException', 'PhutilArgumentSpecificationTestCase' => 'ArcanistPhutilTestCase', 'PhutilArgumentUsageException' => 'PhutilArgumentParserException', 'PhutilBufferedIterator' => 'Iterator', 'PhutilBufferedIteratorExample' => 'PhutilBufferedIterator', 'PhutilBufferedIteratorTestCase' => 'ArcanistPhutilTestCase', 'PhutilChannelChannel' => 'PhutilChannel', 'PhutilConsoleServerChannel' => 'PhutilChannelChannel', 'PhutilConsoleStdinNotInteractiveException' => 'Exception', 'PhutilConsoleWrapTestCase' => 'ArcanistPhutilTestCase', 'PhutilDefaultSyntaxHighlighterEngine' => 'PhutilSyntaxHighlighterEngine', 'PhutilDefaultSyntaxHighlighterEnginePygmentsFuture' => 'FutureProxy', 'PhutilDefaultSyntaxHighlighterEngineTestCase' => 'ArcanistPhutilTestCase', 'PhutilDeferredLogTestCase' => 'ArcanistPhutilTestCase', 'PhutilDocblockParserTestCase' => 'ArcanistPhutilTestCase', 'PhutilEmailAddressTestCase' => 'ArcanistPhutilTestCase', 'PhutilEventType' => 'PhutilEventConstants', 'PhutilExcessiveServiceCallsDaemon' => 'PhutilTortureTestDaemon', 'PhutilExecChannel' => 'PhutilChannel', 'PhutilFatalDaemon' => 'PhutilTortureTestDaemon', 'PhutilFileLock' => 'PhutilLock', 'PhutilFileLockTestCase' => 'ArcanistPhutilTestCase', 'PhutilGitURITestCase' => 'ArcanistPhutilTestCase', 'PhutilHangForeverDaemon' => 'PhutilTortureTestDaemon', 'PhutilHelpArgumentWorkflow' => 'PhutilArgumentWorkflow', 'PhutilJSONProtocolChannel' => 'PhutilProtocolChannel', 'PhutilJSONProtocolChannelTestCase' => 'ArcanistPhutilTestCase', 'PhutilJSONTestCase' => 'ArcanistPhutilTestCase', 'PhutilLanguageGuesserTestCase' => 'ArcanistPhutilTestCase', + 'PhutilLexerSyntaxHighlighter' => 'PhutilSyntaxHighlighter', 'PhutilLockException' => 'Exception', 'PhutilMarkupTestCase' => 'ArcanistPhutilTestCase', 'PhutilMissingSymbolException' => 'Exception', 'PhutilNiceDaemon' => 'PhutilTortureTestDaemon', 'PhutilOpaqueEnvelopeTestCase' => 'ArcanistPhutilTestCase', 'PhutilPHPFragmentLexer' => 'PhutilLexer', + 'PhutilPHPFragmentLexerHighlighterTestCase' => 'ArcanistPhutilTestCase', 'PhutilPHPFragmentLexerTestCase' => 'ArcanistPhutilTestCase', 'PhutilPHPObjectProtocolChannel' => 'PhutilProtocolChannel', 'PhutilPHPObjectProtocolChannelTestCase' => 'ArcanistPhutilTestCase', 'PhutilPHTTestCase' => 'ArcanistPhutilTestCase', 'PhutilPersonTest' => 'PhutilPerson', 'PhutilProcessGroupDaemon' => 'PhutilTortureTestDaemon', 'PhutilProtocolChannel' => 'PhutilChannelChannel', 'PhutilRemarkupEngine' => 'PhutilMarkupEngine', 'PhutilRemarkupEngineRemarkupCodeBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupDefaultBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupHeaderBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupInlineBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupListBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupLiteralBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupNoteBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupQuotesBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineRemarkupTableBlockRule' => 'PhutilRemarkupEngineBlockRule', 'PhutilRemarkupEngineTestCase' => 'ArcanistPhutilTestCase', 'PhutilRemarkupRuleBold' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleDel' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleDocumentLink' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleEscapeHTML' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleEscapeRemarkup' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleHyperlink' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleItalic' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleLinebreaks' => 'PhutilRemarkupRule', 'PhutilRemarkupRuleMonospace' => 'PhutilRemarkupRule', 'PhutilSaturateStdoutDaemon' => 'PhutilTortureTestDaemon', 'PhutilSimpleOptionsTestCase' => 'ArcanistPhutilTestCase', 'PhutilSocketChannel' => 'PhutilChannel', 'PhutilSyntaxHighlighterException' => 'Exception', 'PhutilTortureTestDaemon' => 'PhutilDaemon', 'PhutilTranslatorTestCase' => 'ArcanistPhutilTestCase', 'PhutilURITestCase' => 'ArcanistPhutilTestCase', 'PhutilUTF8TestCase' => 'ArcanistPhutilTestCase', 'PhutilUtilsTestCase' => 'ArcanistPhutilTestCase', 'PhutilXHPASTSyntaxHighlighterTestCase' => 'ArcanistPhutilTestCase', 'TestAbstractDirectedGraph' => 'AbstractDirectedGraph', 'XHPASTNode' => 'AASTNode', 'XHPASTSyntaxErrorException' => 'Exception', 'XHPASTToken' => 'AASTToken', 'XHPASTTree' => 'AASTTree', 'XHPASTTreeTestCase' => 'ArcanistPhutilTestCase', ), )); diff --git a/src/lexer/PhutilPHPFragmentLexer.php b/src/lexer/PhutilPHPFragmentLexer.php index bfd771a..f85361a 100644 --- a/src/lexer/PhutilPHPFragmentLexer.php +++ b/src/lexer/PhutilPHPFragmentLexer.php @@ -1,304 +1,304 @@ array( array('<\\?(php)?', 'cp', 'php'), array('[^<]+', null), array('<', null), ), 'php' => array_merge(array( array('\\?>', 'cp', '!pop'), array( '<<<([\'"]?)('.$identifier_pattern.')\\1\\n.*?\\n\\2\\;?\\n', 's'), ), $nonsemantic_rules, array( array('__halt_compiler\\b', 'cp', 'halt_compiler'), array('(->|::)', 'o', 'attr'), array('[~!%^&*+=|:.<>/?@-]+', 'o'), array('[\\[\\]{}();,]', 'o'), // After 'new', try to match an unadorned symbol. array('(new|instanceof)\\b', 'k', 'possible_classname', array( 'case-insensitive' => true, ), ), array('function\\b', 'k', 'function_definition', array( 'case-insensitive' => true, ), ), // After 'extends' or 'implements', match a list of classes/interfaces. array('(extends|implements)\\b', 'k', 'class_list', array( 'case-insensitive' => true, ), ), array('catch\\b', 'k', 'catch', array( 'case-insensitive' => true, ), ), array('('.implode('|', $keywords).')\\b', 'k', null, array( 'case-insensitive' => true, )), array('('.implode('|', $constants).')\\b', 'kc', null, array( 'case-insensitive' => true, )), array('\\$+'.$identifier_pattern, 'nv'), // Match "f(" as a function and "C::" as a class. These won't work // if you put a comment between the symbol and the operator, but // that's a bizarre usage. array($identifier_ns_pattern.'(?=\s*[\\(])', 'nf'), array($identifier_ns_pattern.'(?=\s*::)', 'nc', 'context_attr', array( 'context' => 'push', ), ), array($identifier_ns_pattern, 'no'), array('(\\d+\\.\\d*|\\d*\\.\\d+)([eE][+-]?[0-9]+)?', 'mf'), array('\\d+[eE][+-]?[0-9]+', 'mf'), array('0[0-7]+', 'mo'), array('0[xX][a-fA-F0-9]+', 'mh'), array('0[bB][0-1]+', 'm'), array('\d+', 'mi'), array("'", "s1", 'string1'), array("`", "sb", 'stringb'), array('"', 's2', 'string2'), array('.', null), )), // We've just matched a class name, with a "::" lookahead. The name of // the class is on the top of the context stack. We want to try to match // the attribute or method (e.g., "X::C" or "X::f()"). 'context_attr' => array_merge($nonsemantic_rules, array( array('::', 'o'), array($identifier_pattern.'(?=\s*[\\(])', 'nf', '!pop', array( 'context' => 'pop', ), ), array($identifier_pattern, 'na', '!pop', array( 'context' => 'pop', ), ), array('', null, '!pop', array( 'context' => 'discard', ), ), )), // After '->' or '::', a symbol is an attribute name. Note that we end // up in 'context_attr' instead of here in some cases. 'attr' => array_merge($nonsemantic_rules, array( array($identifier_pattern, 'na', '!pop'), array('', null, '!pop'), )), // After 'new', a symbol is a class name. 'possible_classname' => array_merge($nonsemantic_rules, array( array($identifier_ns_pattern, 'nc', '!pop'), array('', null, '!pop'), )), 'string1' => array( array('[^\'\\\\]+', 's1'), array("'", 's1', '!pop'), array('\\\\.', 'k'), ), 'stringb' => array( array('[^`\\\\]+', 'sb'), array('`', 'sb', '!pop'), array('\\\\.', 'k'), ), 'string2' => array( array('[^"\\\\]+', 's2'), array('"', 's2', '!pop'), array('\\\\.', 'k'), ), // In a function definition (after "function"), we don't link the name // as a "nf" (name.function) since it is its own definition. 'function_definition' => array_merge($nonsemantic_rules, array( array('&', 'o'), array('\\(', 'o', '!pop'), array('[a-zA-Z_][a-zA-Z0-9_]*', 'no', '!pop'), )), // For "//" and "#" comments, we need to break out if we see "?" followed // by ">". 'line_comment' => array( array('[^?\\n]+', 'c'), array('\\n', null, '!pop'), array('(?=\\?>)', null, '!pop'), - array('\\?', 'c', '!pop'), + array('\\?', 'c'), ), // We've seen __halt_compiler. Grab the '();' afterward and then eat // the rest of the file as raw data. 'halt_compiler' => array_merge($nonsemantic_rules, array( array('[()]', 'o'), array(';', 'o', 'compiler_halted'), array('\\?>', 'o', 'compiler_halted'), // Just halt on anything else. array('', null, 'compiler_halted'), )), // __halt_compiler has taken effect. 'compiler_halted' => array( array('.+', null), ), 'class_list' => array_merge($nonsemantic_rules, array( array(',', 'o'), array('implements', 'k'), array($identifier_ns_pattern, 'nc'), array('', null, '!pop'), )), 'catch' => array_merge($nonsemantic_rules, array( array('\\(', 'o'), array($identifier_ns_pattern, 'nc'), array('', null, '!pop'), )), ); } } diff --git a/src/markup/syntax/engine/PhutilDefaultSyntaxHighlighterEngine.php b/src/markup/syntax/engine/PhutilDefaultSyntaxHighlighterEngine.php index e38213b..6812fae 100644 --- a/src/markup/syntax/engine/PhutilDefaultSyntaxHighlighterEngine.php +++ b/src/markup/syntax/engine/PhutilDefaultSyntaxHighlighterEngine.php @@ -1,100 +1,106 @@ config[$key] = $value; return $this; } public function getLanguageFromFilename($filename) { static $default_map = array( // All files which have file extensions that we haven't already matched // map to their extensions. '@\\.([^./]+)$@' => 1, ); $maps = array(); if (!empty($this->config['filename.map'])) { $maps[] = $this->config['filename.map']; } $maps[] = $default_map; foreach ($maps as $map) { foreach ($map as $regexp => $lang) { $matches = null; if (preg_match($regexp, $filename, $matches)) { if (is_numeric($lang)) { return idx($matches, $lang); } else { return $lang; } } } } return null; } public function getHighlightFuture($language, $source) { if ($language === null) { $language = PhutilLanguageGuesser::guessLanguage($source); } $have_pygments = !empty($this->config['pygments.enabled']); if ($language == 'php' && xhpast_is_available()) { return id(new PhutilXHPASTSyntaxHighlighter()) - ->setConfig('pygments.enabled', $have_pygments) ->getHighlightFuture($source); } if ($language == 'console') { return id(new PhutilConsoleSyntaxHighlighter()) ->getHighlightFuture($source); } if ($language == 'diviner' || $language == 'remarkup') { return id(new PhutilDivinerSyntaxHighlighter()) ->getHighlightFuture($source); } if ($language == 'rainbow') { return id(new PhutilRainbowSyntaxHighlighter()) ->getHighlightFuture($source); } + if ($language == 'php') { + return id(new PhutilLexerSyntaxHighlighter()) + ->setConfig('lexer', new PhutilPHPFragmentLexer()) + ->setConfig('language', 'php') + ->getHighlightFuture($source); + } + if ($have_pygments) { return id(new PhutilPygmentsSyntaxHighlighter()) ->setConfig('language', $language) ->getHighlightFuture($source); } return id(new PhutilDefaultSyntaxHighlighter()) ->getHighlightFuture($source); } } diff --git a/src/markup/syntax/highlighter/PhutilLexerSyntaxHighlighter.php b/src/markup/syntax/highlighter/PhutilLexerSyntaxHighlighter.php new file mode 100644 index 0000000..58a952b --- /dev/null +++ b/src/markup/syntax/highlighter/PhutilLexerSyntaxHighlighter.php @@ -0,0 +1,99 @@ +config[$key] = $value; + return $this; + } + + public function getHighlightFuture($source) { + $strip = false; + $state = 'start'; + $lang = idx($this->config, 'language'); + + if ($lang == 'php') { + if (strpos($source, 'config, 'lexer'); + $tokens = $lexer->getTokens($source, $state); + $tokens = $lexer->mergeTokens($tokens); + + $result = array(); + foreach ($tokens as $token) { + list($type, $value, $context) = $token; + $value = phutil_escape_html($value); + + $data_name = null; + switch ($type) { + case 'nc': + case 'nf': + case 'na': + $data_name = ' data-symbol-name="'.$value.'"'; + break; + } + + $data_context = null; + if ($context) { + $context = phutil_escape_html($context); + $data_context = ' data-symbol-context="'.$context.'"'; + } + + $class_name = null; + if ($type) { + $class_name = ' class="'.$type.'"'; + } + + if (strpos($value, "\n") !== false) { + $value = explode("\n", $value); + } else { + $value = array($value); + } + foreach ($value as $part) { + if (strlen($part)) { + if ($class_name) { + $result[] = + ''. + $part. + ''; + } else { + $result[] = $part; + } + } + $result[] = "\n"; + } + + // Throw away the last "\n". + array_pop($result); + } + + $result = implode('', $result); + + return new ImmediateFuture($result); + } + +} diff --git a/src/markup/syntax/highlighter/PhutilXHPASTSyntaxHighlighter.php b/src/markup/syntax/highlighter/PhutilXHPASTSyntaxHighlighter.php index 276922d..bf5d8ed 100644 --- a/src/markup/syntax/highlighter/PhutilXHPASTSyntaxHighlighter.php +++ b/src/markup/syntax/highlighter/PhutilXHPASTSyntaxHighlighter.php @@ -1,257 +1,252 @@ config[$key] = $value; return $this; } public function getHighlightFuture($source) { try { $result = $this->applyXHPHighlight($source); return new ImmediateFuture($result); } catch (Exception $ex) { - if (!empty($this->config['pygments.enabled'])) { - // Fall back to Pygments if we failed to highlight using XHP. The XHP - // highlighter currently uses a parser, not just a lexer, so it fails on - // snippets which aren't valid syntactically. - return id(new PhutilPygmentsSyntaxHighlighter()) - ->setConfig('language', 'php') - ->getHighlightFuture($source); - } else { - return id(new PhutilDefaultSyntaxHighlighter()) - ->getHighlightFuture($source); - } + // XHP can't highlight source that isn't syntactically valid. Fall back + // to the fragment lexer. + return id(new PhutilLexerSyntaxHighlighter()) + ->setConfig('lexer', new PhutilPHPFragmentLexer()) + ->setConfig('language', 'php') + ->getHighlightFuture($source); } } private function applyXHPHighlight($source) { // We perform two passes here: one using the AST to find symbols we care // about -- particularly, class names and function names. These are used // in the crossreference stuff to link into Diffusion. After we've done our // AST pass, we do a followup pass on the token stream to catch all the // simple stuff like strings and comments. $scrub = false; if (strpos($source, 'getRootNode(); $tokens = $root->getTokens(); $interesting_symbols = $this->findInterestingSymbols($root); $out = array(); foreach ($tokens as $key => $token) { $value = phutil_escape_html($token->getValue()); $class = null; $multi = false; $attrs = ''; if (isset($interesting_symbols[$key])) { $sym = $interesting_symbols[$key]; $class = $sym[0]; if (isset($sym['context'])) { $attrs = $attrs.' data-symbol-context="'.$sym['context'].'"'; } if (isset($sym['symbol'])) { $attrs = $attrs.' data-symbol-name="'.$sym['symbol'].'"'; } } else { switch ($token->getTypeName()) { case 'T_WHITESPACE': break; case 'T_DOC_COMMENT': $class = 'dc'; $multi = true; break; case 'T_COMMENT': $class = 'c'; $multi = true; break; case 'T_CONSTANT_ENCAPSED_STRING': case 'T_ENCAPSED_AND_WHITESPACE': case 'T_INLINE_HTML': $class = 's'; $multi = true; break; case 'T_VARIABLE': $class = 'nv'; break; case 'T_OPEN_TAG': case 'T_OPEN_TAG_WITH_ECHO': case 'T_CLOSE_TAG': $class = 'o'; break; case 'T_LNUMBER': case 'T_DNUMBER': $class = 'm'; break; case 'T_STRING': static $magic = array( 'true' => true, 'false' => true, 'null' => true, ); if (isset($magic[$value])) { $class = 'k'; break; } $class = 'nx'; break; default: $class = 'k'; break; } } if ($class) { $l = ''; $r = ''; $value = $l.$value.$r; if ($multi) { // If the token may have multiple lines in it, make sure each // crosses no more than one line so the lines can be put // in a table, etc., later. $value = str_replace( "\n", $r."\n".$l, $value); } $out[] = $value; } else { $out[] = $value; } } if ($scrub) { array_shift($out); } return rtrim(implode('', $out)); } private function findInterestingSymbols(XHPASTNode $root) { // Class name symbols appear in: // class X extends X implements X, X { ... } // new X(); // $x instanceof X // catch (X $x) // function f(X $x) // X::f(); // X::$m; // X::CONST; // These are PHP builtin tokens which can appear in a classname context. // Don't link them since they don't go anywhere useful. static $builtin_class_tokens = array( 'self' => true, 'parent' => true, 'static' => true, ); // Fortunately XHPAST puts all of these in a special node type so it's // easy to find them. $result_map = array(); $class_names = $root->selectDescendantsOfType('n_CLASS_NAME'); foreach ($class_names as $class_name) { foreach ($class_name->getTokens() as $key => $token) { if (isset($builtin_class_tokens[$token->getValue()])) { // This is something like "self::method()". continue; } $result_map[$key] = array( 'nc', // "Name, Class" 'symbol' => $class_name->getConcreteString(), ); } } // Function name symbols appear in: // f() $function_calls = $root->selectDescendantsOfType('n_FUNCTION_CALL'); foreach ($function_calls as $call) { $call = $call->getChildByIndex(0); if ($call->getTypeName() == 'n_SYMBOL_NAME') { // This is a normal function call, not some $f() shenanigans. foreach ($call->getTokens() as $key => $token) { $result_map[$key] = array( 'nf', // "Name, Function" 'symbol' => $call->getConcreteString(), ); } } } // Upon encountering $x->y, link y without context, since $x is unknown. $prop_access = $root->selectDescendantsOfType('n_OBJECT_PROPERTY_ACCESS'); foreach ($prop_access as $access) { $right = $access->getChildByIndex(1); if ($right->getTypeName() == 'n_INDEX_ACCESS') { // otherwise $x->y[0] doesn't get highlighted $right = $right->getChildByIndex(0); } if ($right->getTypeName() == 'n_STRING') { foreach ($right->getTokens() as $key => $token) { $result_map[$key] = array( 'na', // "Name, Attribute" 'symbol' => $right->getConcreteString(), ); } } } // Upon encountering x::y, try to link y with context x. $static_access = $root->selectDescendantsOfType('n_CLASS_STATIC_ACCESS'); foreach ($static_access as $access) { $class = $access->getChildByIndex(0); $right = $access->getChildByIndex(1); if ($class->getTypeName() == 'n_CLASS_NAME' && ($right->getTypeName() == 'n_STRING' || $right->getTypeName() == 'n_VARIABLE')) { $classname = head($class->getTokens())->getValue(); $result = array( 'na', 'symbol' => ltrim($right->getConcreteString(), '$'), ); if (!isset($builtin_class_tokens[$classname])) { $result['context'] = $classname; } foreach ($right->getTokens() as $key => $token) { $result_map[$key] = $result; } } } return $result_map; } } diff --git a/src/markup/syntax/highlighter/__tests__/PhutilPHPFragmentLexerHighlighterTestCase.php b/src/markup/syntax/highlighter/__tests__/PhutilPHPFragmentLexerHighlighterTestCase.php new file mode 100644 index 0000000..1e7fd84 --- /dev/null +++ b/src/markup/syntax/highlighter/__tests__/PhutilPHPFragmentLexerHighlighterTestCase.php @@ -0,0 +1,45 @@ +setConfig('language', 'php'); + $highlighter->setConfig('lexer', new PhutilPHPFragmentLexer()); + + + $path = dirname(__FILE__).'/phpfragment/'; + foreach (Filesystem::listDirectory($path, $include_hidden = false) as $f) { + if (preg_match('/.test$/', $f)) { + $expect = preg_replace('/.test$/', '.expect', $f); + $source = Filesystem::readFile($path.'/'.$f); + + $this->assertEqual( + Filesystem::readFile($path.'/'.$expect), + $highlighter->getHighlightFuture($source)->resolve(), + $f); + } + } + } + +} diff --git a/src/markup/syntax/highlighter/__tests__/phpfragment/abuse.expect b/src/markup/syntax/highlighter/__tests__/phpfragment/abuse.expect new file mode 100644 index 0000000..aa31ec0 --- /dev/null +++ b/src/markup/syntax/highlighter/__tests__/phpfragment/abuse.expect @@ -0,0 +1,16 @@ +<? + +// comment? comment! ?> + +data + +<?php + +__halt_compiler /* ! */ ( // ) +) /* ;;;; */ + +; + +data data +<?php +data \ No newline at end of file diff --git a/src/markup/syntax/highlighter/__tests__/phpfragment/abuse.test b/src/markup/syntax/highlighter/__tests__/phpfragment/abuse.test new file mode 100644 index 0000000..b3ade94 --- /dev/null +++ b/src/markup/syntax/highlighter/__tests__/phpfragment/abuse.test @@ -0,0 +1,16 @@ + + +data + +public function f() { + ExampleClass::EXAMPLE_CONSTANT; + ExampleClass::exampleMethod(); + example_function(); +} diff --git a/src/markup/syntax/highlighter/__tests__/phpfragment/basics.test b/src/markup/syntax/highlighter/__tests__/phpfragment/basics.test new file mode 100644 index 0000000..87e3f9a --- /dev/null +++ b/src/markup/syntax/highlighter/__tests__/phpfragment/basics.test @@ -0,0 +1,5 @@ +public function f() { + ExampleClass::EXAMPLE_CONSTANT; + ExampleClass::exampleMethod(); + example_function(); +}