diff --git a/BUILD.gn b/BUILD.gn index 86f1232bb66..daa3b1e4e83 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -167,7 +167,6 @@ generated_non_autostart_non_remote_modules = [ "$resources_out_dir/timeline_model/timeline_model_module.js", "$resources_out_dir/timeline/timeline_module.js", "$resources_out_dir/web_audio/web_audio_module.js", - "$resources_out_dir/dirac_lazy/dirac_lazy_module.js", "$resources_out_dir/webauthn/webauthn_module.js", "$resources_out_dir/workspace_diff/workspace_diff_module.js", "$resources_out_dir/emulated_devices/emulated_devices_module.js", diff --git a/all_devtools_files.gni b/all_devtools_files.gni index f9479c441fd..a0f5b507d73 100644 --- a/all_devtools_files.gni +++ b/all_devtools_files.gni @@ -11,14 +11,6 @@ all_devtools_files = [ "front_end/console/dirac-codemirror.css", "front_end/console/dirac-theme.css", "front_end/console/dirac-prompt.css", - "front_end/dirac/module.json", - "front_end/dirac/parinfer.js", - "front_end/dirac/parinfer-codemirror.js", - "front_end/dirac/keysim.js", - "front_end/dirac/dirac.js", - "front_end/dirac/require-implant.js", - "front_end/dirac_lazy/module.json", - "front_end/dirac_lazy/dirac_lazy.js", # dirac - end "front_end/shell.js", "front_end/accessibility_test_runner/accessibility_test_runner.js", @@ -111,6 +103,9 @@ all_devtools_files = [ "front_end/device_mode_test_runner/module.json", "front_end/devices/module.json", "front_end/diff/module.json", + # dirac start + "front_end/dirac/module.json", + # dirac end "front_end/dom_extension/module.json", "front_end/elements_test_runner/elements_test_runner.js", "front_end/elements_test_runner/EditDOMTestRunner.js", diff --git a/all_devtools_modules.gni b/all_devtools_modules.gni index 9d3c1cb4a06..ead742ff9e2 100644 --- a/all_devtools_modules.gni +++ b/all_devtools_modules.gni @@ -7,14 +7,6 @@ import("./scripts/build/ninja/vars.gni") generated_typescript_modules = [] all_typescript_module_sources = [ - # dirac - start -# "dirac/parinfer.js", -# "dirac/parinfer-codemirror.js", -# "dirac/keysim.js", - "third_party/codemirror/package/addon/runmode/runmode.js", - "third_party/codemirror/package/addon/display/placeholder.js", - "console/ConsoleDiracPrompt.js", - # dirac - end "accessibility/ARIAAttributesView.js", "accessibility/ARIAMetadata.js", "accessibility/AXBreadcrumbsPane.js", @@ -129,6 +121,16 @@ all_typescript_module_sources = [ "devices/DevicesView.js", "diff/DiffWrapper.js", "diff/diff_match_patch.js", + # dirac - start + "dirac/dirac.js", + "dirac/DiracAngel.js", + "dirac/parinfer.js", + "dirac/parinfer-codemirror.js", + "dirac/keysim.js", + "third_party/codemirror/package/addon/runmode/runmode.js", + "third_party/codemirror/package/addon/display/placeholder.js", + "console/ConsoleDiracPrompt.js", + # dirac - end "dom_extension/DOMExtension.js", "elements/Adorner.js", "elements/CSSAngle.js", diff --git a/devtools_grd_files.gni b/devtools_grd_files.gni index 51d9bb5587a..c6ede852415 100644 --- a/devtools_grd_files.gni +++ b/devtools_grd_files.gni @@ -10,13 +10,6 @@ # are missed from the GRD. grd_files_release_sources = [ - # dirac - start -# "front_end/dirac/dirac.js", -# "front_end/dirac/parinfer.js", -# "front_end/dirac/parinfer-codemirror.js", -# "front_end/dirac/keysim.js", -# "front_end/dirac_lazy/dirac_lazy_module.js", - # dirac - end "front_end/Images/accelerometer-back.svg", "front_end/Images/accelerometer-bottom.png", "front_end/Images/accelerometer-front.svg", @@ -142,6 +135,7 @@ grd_files_release_sources = [ "front_end/diff/diff-legacy.js", "front_end/diff/diff.js", "front_end/diff/diff_module.js", + "front_end/dirac/dirac.js", "front_end/dom_extension/dom_extension.js", "front_end/elements/elements-legacy.js", "front_end/elements/elements.js", @@ -460,6 +454,9 @@ grd_files_debug_sources = [ "front_end/console/ConsoleView.js", "front_end/console/ConsoleViewMessage.js", "front_end/console/ConsoleViewport.js", + # dirac start + "front_end/console/ConsoleDiracPrompt.js", + # dirac end "front_end/console_counters/WarningErrorCounter.js", "front_end/cookie_table/CookiesTable.js", "front_end/coverage/CoverageDecorationManager.js", @@ -483,6 +480,12 @@ grd_files_debug_sources = [ "front_end/devices/DevicesView.js", "front_end/diff/DiffWrapper.js", "front_end/diff/diff_match_patch.js", + # dirac start + "front_end/dirac/DiracAngel.js", + "front_end/dirac/parinfer.js", + "front_end/dirac/parinfer-codemirror.js", + "front_end/dirac/keysim.js", + # dirac end "front_end/dom_extension/DOMExtension.js", "front_end/elements/Adorner.js", "front_end/elements/CSSAngle.js", diff --git a/devtools_module_entrypoints.gni b/devtools_module_entrypoints.gni index bb1e3c5a217..1bd556989c7 100644 --- a/devtools_module_entrypoints.gni +++ b/devtools_module_entrypoints.gni @@ -55,6 +55,7 @@ generated_typescript_entrypoint_sources = [ "$resources_out_dir/devices/devices.js", "$resources_out_dir/diff/diff-legacy.js", "$resources_out_dir/diff/diff.js", + "$resources_out_dir/dirac/dirac.js", "$resources_out_dir/dom_extension/dom_extension.js", "$resources_out_dir/elements/elements-legacy.js", "$resources_out_dir/elements/elements.js", diff --git a/front_end/BUILD.gn b/front_end/BUILD.gn index 3587a9862f3..9918a81d227 100644 --- a/front_end/BUILD.gn +++ b/front_end/BUILD.gn @@ -34,6 +34,7 @@ group("front_end") { "developer_resources:bundle", "devices:bundle", "diff:bundle", + "dirac:bundle", "dom_extension:bundle", "elements:bundle", "emulated_devices/optimized:optimized_emulated_devices", diff --git a/front_end/accessibility/ARIAAttributesView.js b/front_end/accessibility/ARIAAttributesView.js index f6deef84c95..43b76f71c5e 100644 --- a/front_end/accessibility/ARIAAttributesView.js +++ b/front_end/accessibility/ARIAAttributesView.js @@ -1,4 +1,3 @@ -// @ts-nocheck // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/front_end/bindings/ResourceScriptMapping.js b/front_end/bindings/ResourceScriptMapping.js index 7b1c8a138e0..713914c5bbf 100644 --- a/front_end/bindings/ResourceScriptMapping.js +++ b/front_end/bindings/ResourceScriptMapping.js @@ -297,13 +297,6 @@ export class ResourceScriptFile extends Common.ObjectWrapper.ObjectWrapper { Workspace.UISourceCode.Events.WorkingCopyCommitted, this._workingCopyCommitted, this); } - /** - * @return {?SDK.Script.Script} - */ - getScript() { - return this._script; - } - /** * @param {!Array.} scripts * @return {boolean} diff --git a/front_end/common/common.js b/front_end/common/common.js index 21b8f59b669..b49085fcaeb 100644 --- a/front_end/common/common.js +++ b/front_end/common/common.js @@ -40,6 +40,14 @@ export {UIString} from '../platform/platform.js'; export const ls = Platform.UIString.ls; +function getDiracAngel() { + // @ts-ignore + const angel = globalThis.diracAngel; + if (!angel) { + throw 'getDiracAngel called too early'; + } + return angel; +} /** * @type {!Settings.Settings} @@ -75,4 +83,5 @@ export { Trie, Worker, WasmDisassembly, + getDiracAngel }; diff --git a/front_end/components/Linkifier.js b/front_end/components/Linkifier.js index 65370900174..813401f2ced 100644 --- a/front_end/components/Linkifier.js +++ b/front_end/components/Linkifier.js @@ -1,4 +1,3 @@ -// @ts-nocheck /* * Copyright (C) 2012 Google Inc. All rights reserved. * @@ -845,10 +844,12 @@ export class Linkifier { result.push(action); } } - if (dirac.hasLinkActions) { - const diracAction = Components.Linkifier.diracLinkHandlerAction; + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.hasLinkActions) { + const diracAction = diracAngel.diracLinkHandlerAction; if (diracAction) { result.unshift({ + section: 'reveal', title: diracAction.title, handler: diracAction.handler.bind(null, result, contentProvider.contentURL(), lineNumber, columnNumber) }); diff --git a/front_end/components/components-legacy.js b/front_end/components/components-legacy.js index 68025679380..bcc7a87d418 100644 --- a/front_end/components/components-legacy.js +++ b/front_end/components/components-legacy.js @@ -29,9 +29,6 @@ Components.Linkifier.LinkHandlerSettingUI = ComponentsModule.Linkifier.LinkHandl /** @constructor */ Components.Linkifier.ContentProviderContextMenuProvider = ComponentsModule.Linkifier.ContentProviderContextMenuProvider; -/** @type {?Object} */ -Components.Linkifier.diracLinkHandlerAction = null; - /** @interface */ Components.LinkDecorator = ComponentsModule.Linkifier.LinkDecorator; diff --git a/front_end/console/BUILD.gn b/front_end/console/BUILD.gn index 1d2db0cf664..895ec48b8d7 100644 --- a/front_end/console/BUILD.gn +++ b/front_end/console/BUILD.gn @@ -30,6 +30,7 @@ devtools_module("console") { "../platform:bundle", "../sdk:bundle", "../text_utils:bundle", + "../text_editor:bundle", # for dirac "../theme_support:bundle", "../ui:bundle", ] diff --git a/front_end/console/ConsoleDiracPrompt.js b/front_end/console/ConsoleDiracPrompt.js index 5dfc76a4090..93b948a2816 100644 --- a/front_end/console/ConsoleDiracPrompt.js +++ b/front_end/console/ConsoleDiracPrompt.js @@ -1,10 +1,15 @@ -// @ts-nocheck // Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/* eslint-disable no-console */ + +// @ts-nocheck import {ConsoleHistoryManager} from './ConsolePrompt.js'; import * as UI from '../ui/ui.js'; +import * as TextEditor from '../text_editor/text_editor.js'; +import * as ObjectUI from '../object_ui/object_ui.js'; +import * as Common from '../common/common.js'; /** * @unrestricted @@ -19,11 +24,15 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { this._history = new ConsoleHistoryManager(); this._codeMirror = codeMirrorInstance; + // @ts-ignore this._codeMirror.on('changes', this._changes.bind(this)); + // @ts-ignore this._codeMirror.on('scroll', this._onScroll.bind(this)); + // @ts-ignore this._codeMirror.on('cursorActivity', this._onCursorActivity.bind(this)); + // @ts-ignore this._codeMirror.on('blur', this._blur.bind(this)); - this._currentClojureScriptNamespace = null; + this._currentClojureScriptNamespace = 'cljs.user'; this._lastAutocompleteRequest = 0; // just to mimic disabled eager preview functionality of ConsolePrompt this._eagerPreviewElement = document.createElement('div'); @@ -49,6 +58,7 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @return {boolean} */ hasFocus() { + // @ts-ignore return this._codeMirror.hasFocus(); } @@ -56,12 +66,17 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @override */ focus() { + // @ts-ignore this._codeMirror.focus(); // HACK: this is needed to properly display cursor in empty codemirror: // http://stackoverflow.com/questions/10575833/codemirror-has-content-but-wont-display-until-keypress + // @ts-ignore this._codeMirror.refresh(); } + /** + * @param {string} ns + */ setCurrentClojureScriptNamespace(ns) { this._currentClojureScriptNamespace = ns; } @@ -71,6 +86,7 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @return {string} */ text() { + // @ts-ignore const text = this._codeMirror.getValue(); return text.replace(/[\s\n]+$/gm, ''); // remove trailing newlines and whitespace } @@ -81,9 +97,12 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { */ setText(x) { this.clearAutocomplete(); + // @ts-ignore this._codeMirror.setValue(x); this.moveCaretToEndOfPrompt(); - this._element.scrollIntoView(); + if (this._element) { + this._element.scrollIntoView(); + } } /** @@ -102,6 +121,7 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @return {boolean} */ isCaretInsidePrompt() { + // @ts-ignore return this._codeMirror.hasFocus(); } @@ -110,8 +130,11 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @return {boolean} */ _isCaretAtEndOfPrompt() { + // @ts-ignore const content = this._codeMirror.getValue(); + // @ts-ignore const cursor = this._codeMirror.getCursor(); + // @ts-ignore const endCursor = this._codeMirror.posFromIndex(content.length); return (cursor.line === endCursor.line && cursor.ch === endCursor.ch); } @@ -120,7 +143,9 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @return {boolean} */ isCaretOnFirstLine() { + // @ts-ignore const cursor = this._codeMirror.getCursor(); + // @ts-ignore return (cursor.line === this._codeMirror.firstLine()); } @@ -128,7 +153,9 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @return {boolean} */ isCaretOnLastLine() { + // @ts-ignore const cursor = this._codeMirror.getCursor(); + // @ts-ignore return (cursor.line === this._codeMirror.lastLine()); } @@ -137,19 +164,24 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @override */ moveCaretToEndOfPrompt() { + // @ts-ignore this._codeMirror.setCursor(this._codeMirror.lastLine() + 1, 0, null); } /** * @override + * @param {number} index */ moveCaretToIndex(index) { + // @ts-ignore const pos = this._codeMirror.posFromIndex(index); + // @ts-ignore this._codeMirror.setCursor(pos, null, null); } finishAutocomplete() { - if (dirac.DEBUG_COMPLETIONS) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('finishAutocomplete', (new Error()).stack); } this.clearAutocomplete(); @@ -159,7 +191,7 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { /** * @param {!CodeMirror} codeMirror - * @param {!Array.} changes + * @param {!Array.} changes */ _changes(codeMirror, changes) { if (!changes.length) { @@ -172,7 +204,8 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { singleCharInput = (changeObject.origin === '+input' && changeObject.text.length === 1 && changeObject.text[0].length === 1) || (this._isSuggestBoxVisible() && changeObject.origin === '+delete' && changeObject.removed.length === 1 && changeObject.removed[0].length === 1); } - if (dirac.DEBUG_COMPLETIONS) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('_changes', singleCharInput, changes); } if (singleCharInput) { @@ -191,15 +224,21 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { return; } + // @ts-ignore const cursor = this._codeMirror.getCursor(); + // @ts-ignore const scrollInfo = this._codeMirror.getScrollInfo(); + // @ts-ignore const topmostLineNumber = this._codeMirror.lineAtHeight(scrollInfo.top, 'local'); + // @ts-ignore const bottomLine = this._codeMirror.lineAtHeight(scrollInfo.top + scrollInfo.clientHeight, 'local'); if (cursor.line < topmostLineNumber || cursor.line > bottomLine) { this.finishAutocomplete(); } else { this._updateAnchorBox(); - this._suggestBox.setPosition(this._anchorBox); + if (this._suggestBox && this._anchorBox) { + this._suggestBox.setPosition(this._anchorBox); + } } } @@ -213,6 +252,7 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { return; } + // @ts-ignore const cursor = this._codeMirror.getCursor(); if (this._prefixRange) { if (cursor.line !== this._prefixRange.startLine || @@ -232,7 +272,8 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { async complete(force) { // override with empty implementation to disable TextPrompt's autocomplete implementation // we use CodeMirror's changes modelled after TextEditorAutocompleteController.js in DiracPrompt - if (dirac.DEBUG_COMPLETIONS) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('complete called => skip for disabling default auto-complete system'); } } @@ -257,6 +298,9 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { return []; } + /** + * @param {string} prefix + */ _javascriptCompletionTest(prefix) { // test if prefix starts with "js/", then we treat it as javascript completion const m = prefix.match(/^js\/(.*)/); @@ -266,6 +310,7 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { offset: 3 }; } + return null; } /** @@ -279,30 +324,35 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { this._lastAutocompleteRequest++; let shouldExit = false; + // @ts-ignore const cursor = this._codeMirror.getCursor(); + // @ts-ignore const token = this._codeMirror.getTokenAt(cursor); - if (dirac.DEBUG_COMPLETIONS) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('autocomplete:', cursor, token); } if (!token) { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('no autocomplete because no token'); } shouldExit = true; - } else if (this._codeMirror.somethingSelected()) { - if (dirac.DEBUG_COMPLETIONS) { - console.log('no autocomplete because codeMirror.somethingSelected()'); - } - shouldExit = true; - } else if (!force) { - if (token.end !== cursor.ch) { - if (dirac.DEBUG_COMPLETIONS) { - console.log('no autocomplete because cursor is not at the end of detected token'); - } - shouldExit = true; - } + } else { // @ts-ignore + if (this._codeMirror.somethingSelected()) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { + console.log('no autocomplete because codeMirror.somethingSelected()'); + } + shouldExit = true; + } else if (!force) { + if (token.end !== cursor.ch) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { + console.log('no autocomplete because cursor is not at the end of detected token'); + } + shouldExit = true; + } + } } if (shouldExit) { @@ -310,9 +360,10 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { return; } + // @ts-ignore const prefix = this._codeMirror.getRange(new CodeMirror.Pos(cursor.line, token.start), cursor); const javascriptCompletion = this._javascriptCompletionTest(prefix); - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log("detected prefix='" + prefix + "'", javascriptCompletion); } if (javascriptCompletion) { @@ -330,14 +381,15 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @param {number} requestId * @param {string} input * @param {boolean} force - * @param {function(string, string, !UI.SuggestBox.Suggestions)} completionsReadyCallback + * @param {function(string, string, !UI.SuggestBox.Suggestions): null} completionsReadyCallback */ _loadJavascriptCompletions(requestId, input, force, completionsReadyCallback) { - if (dirac.DEBUG_COMPLETIONS) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('_loadJavascriptCompletions', input, force); } if (requestId !== this._lastAutocompleteRequest) { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('_loadJavascriptCompletions cancelled', requestId, this._lastAutocompleteRequest); } return; @@ -372,11 +424,12 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @param {!UI.SuggestBox.Suggestions} completions */ _completionsForJavascriptReady(requestId, reverse, force, expression, prefix, completions) { - if (dirac.DEBUG_COMPLETIONS) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('_completionsForJavascriptReady', prefix, reverse, force, expression, completions); } if (requestId !== this._lastAutocompleteRequest) { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('_completionsForJavascriptReady cancelled', requestId, this._lastAutocompleteRequest); } return; @@ -398,12 +451,14 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { const shouldShowForSingleItem = true; // later maybe implement inline completions like in TextPrompt.js if (this._anchorBox) { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('calling SuggestBox.updateSuggestions', this._anchorBox, completions, shouldShowForSingleItem, this._userEnteredText); } - this._suggestBox.updateSuggestions(this._anchorBox, completions, true, shouldShowForSingleItem, this._userEnteredText); + if (this._suggestBox) { + this._suggestBox.updateSuggestions(this._anchorBox, completions, true, shouldShowForSingleItem, this._userEnteredText); + } } else { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('not calling SuggestBox.updateSuggestions because this._anchorBox is null', completions, shouldShowForSingleItem, this._userEnteredText); } } @@ -415,21 +470,23 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @param {number} requestId * @param {string} input * @param {boolean} force - * @param {function(string, string, !Array., number=)} completionsReadyCallback + * @param {function(string, string, !Array., number=):any} completionsReadyCallback */ _loadClojureScriptCompletions(requestId, input, force, completionsReadyCallback) { - if (dirac.DEBUG_COMPLETIONS) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('_loadClojureScriptCompletions', input, force); } if (requestId !== this._lastAutocompleteRequest) { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('_loadClojureScriptCompletions cancelled', requestId, this._lastAutocompleteRequest); } return; } - const executionContext = self.UI.context.flavor(SDK.ExecutionContext); + const context = UI.Context.Context.instance(); + const executionContext = context.flavor(SDK.ExecutionContext); if (!executionContext) { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.warn('no execution context available'); } completionsReadyCallback('', '', []); @@ -438,7 +495,7 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { const debuggerModel = executionContext.debuggerModel; if (!debuggerModel) { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.warn('no debugger model available'); } completionsReadyCallback('', '', []); @@ -447,10 +504,11 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { const makeSuggestStyle = (style = '') => `suggest-cljs ${style}`; + /** + * @param {string} name + */ const namespaceSelector = name => { - return function (namespaceDescriptors) { - return namespaceDescriptors[name]; - }; + return namespaceDescriptors => namespaceDescriptors[name]; }; const selectCurrentNamespace = namespaceSelector(this._currentClojureScriptNamespace); @@ -487,7 +545,7 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { }); }; - const currentNamespaceDescriptorPromise = dirac.extractNamespacesAsync().then(selectCurrentNamespace); + const currentNamespaceDescriptorPromise = diracAngel.extractNamespacesAsync().then(selectCurrentNamespace); const resolvedNamespaceNamePromise = currentNamespaceDescriptorPromise.then(currentNamespaceDescriptor => { if (!currentNamespaceDescriptor) { @@ -503,7 +561,7 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { return new Promise(resolve => { const resultHandler = (expression, prefix, completions) => { const annotatedCompletions = styleQualifiedSymbols('suggest-cljs-qualified suggest-cljs-pseudo', completions); - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('resultHandler got', expression, prefix, completions, annotatedCompletions); } resolve(annotatedCompletions); @@ -518,34 +576,34 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { const provideCompletionsForNamespace = ([namespaces, namespaceName]) => { const namespace = namespaces[namespaceName]; if (!namespace) { - const macroNamespaceNames = dirac.getMacroNamespaceNames(namespaces); + const macroNamespaceNames = diracAngel.getMacroNamespaceNames(namespaces); if (!macroNamespaceNames.includes(namespaceName)) { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('no known namespace for ', namespaceName); } readyCallback([]); return; } - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('namespace is a macro namespace', namespaceName); } } if (namespace && namespace.pseudo) { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('pseudo namespace => falling back to JS completions', namespaceName); } prepareAnnotatedJavascriptCompletionsForPseudoNamespaceAsync(namespaceName).then(readyCallback); return; } - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('cljs namespace => retrieving symbols and macros from caches', namespaceName); } - const namespaceSymbolsPromise = dirac.extractNamespaceSymbolsAsync(namespaceName) + const namespaceSymbolsPromise = diracAngel.extractNamespaceSymbolsAsync(namespaceName) .then(annotateQualifiedSymbols.bind(this, 'suggest-cljs-qualified')); - const macroNamespaceSymbolsPromise = dirac.extractMacroNamespaceSymbolsAsync(namespaceName) + const macroNamespaceSymbolsPromise = diracAngel.extractMacroNamespaceSymbolsAsync(namespaceName) .then(annotateQualifiedSymbols.bind(this, 'suggest-cljs-qualified suggest-cljs-macro')); // order matters here, see _markAliasedCompletions below @@ -557,7 +615,7 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { Promise.all(jobs).then(concatAnnotatedResults).then(readyCallback); }; - Promise.all([dirac.extractNamespacesAsync(), resolvedNamespaceNamePromise]).then(provideCompletionsForNamespace.bind(this)); + Promise.all([diracAngel.extractNamespacesAsync(), resolvedNamespaceNamePromise]).then(provideCompletionsForNamespace.bind(this)); } else { // general completion (without slashes) // combine: locals (if paused in debugger), current ns symbols, namespace names and cljs.core symbols @@ -571,8 +629,8 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { }; /** - * @param {dirac.ScopeInfo} scopeInfo - * @return {!Array} + * @param {diracAngel.ScopeInfo} scopeInfo + * @return {!Array} */ const extractLocalsFromScopeInfo = scopeInfo => { const locals = []; @@ -642,7 +700,7 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { return []; } - return dirac.extractNamespacesAsync().then(namespaces => { + return diracAngel.extractNamespacesAsync().then(namespaces => { const mapping = namespaceDescriptor[kind] || {}; return Object.keys(mapping).filter(name => name.startsWith(input)).map(name => { const targetName = mapping[name]; @@ -668,17 +726,17 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { })); }; - const localsPromise = dirac.extractScopeInfoFromScopeChainAsync(debuggerModel.selectedCallFrame()).then(extractAndAnnotateLocals); - const currentNamespaceSymbolsPromise = dirac.extractNamespaceSymbolsAsync(this._currentClojureScriptNamespace).then(annotateSymbols.bind(this, 'suggest-cljs-in-ns')); - const namespaceNamesPromise = dirac.extractNamespacesAsync().then(annotateNamespaceNames); - const macroNamespaceNamesPromise = dirac.extractNamespacesAsync().then(dirac.getMacroNamespaceNames).then(annotateMacroNamespaceNames); - const coreNamespaceSymbolsPromise = dirac.extractNamespaceSymbolsAsync('cljs.core').then(annotateSymbols.bind(this, 'suggest-cljs-core')); - const currentNamespaceDescriptor = dirac.extractNamespacesAsync().then(selectCurrentNamespace); + const localsPromise = diracAngel.extractScopeInfoFromScopeChainAsync(debuggerModel.selectedCallFrame()).then(extractAndAnnotateLocals); + const currentNamespaceSymbolsPromise = diracAngel.extractNamespaceSymbolsAsync(this._currentClojureScriptNamespace).then(annotateSymbols.bind(this, 'suggest-cljs-in-ns')); + const namespaceNamesPromise = diracAngel.extractNamespacesAsync().then(annotateNamespaceNames); + const macroNamespaceNamesPromise = diracAngel.extractNamespacesAsync().then(diracAngel.getMacroNamespaceNames).then(annotateMacroNamespaceNames); + const coreNamespaceSymbolsPromise = diracAngel.extractNamespaceSymbolsAsync('cljs.core').then(annotateSymbols.bind(this, 'suggest-cljs-core')); + const currentNamespaceDescriptor = diracAngel.extractNamespacesAsync().then(selectCurrentNamespace); const namespaceAliasesPromise = currentNamespaceDescriptor.then(annotateAliasesOrRefers.bind(this, 'namespaceAliases', 'is ', 'suggest-cljs-ns-alias')); const macroNamespaceAliasesPromise = currentNamespaceDescriptor.then(annotateAliasesOrRefers.bind(this, 'macroNamespaceAliases', 'is ', 'suggest-cljs-ns-alias suggest-cljs-macro')); const namespaceRefersPromise = currentNamespaceDescriptor.then(annotateAliasesOrRefers.bind(this, 'namespaceRefers', 'in ', 'suggest-cljs-refer')); const macroRefersPromise = currentNamespaceDescriptor.then(annotateAliasesOrRefers.bind(this, 'macroRefers', 'in ', 'suggest-cljs-refer suggest-cljs-macro')); - const replSpecialsPromise = dirac.getReplSpecialsAsync().then(annotateReplSpecials); + const replSpecialsPromise = diracAngel.getReplSpecialsAsync().then(annotateReplSpecials); // order matters here, see _markAliasedCompletions below const jobs = [ @@ -707,19 +765,20 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @param {!Array.} completions */ _completionsForClojureScriptReady(requestId, reverse, force, expression, prefix, completions) { - if (dirac.DEBUG_COMPLETIONS) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('_completionsForClojureScriptReady', prefix, reverse, force, completions); } if (requestId !== this._lastAutocompleteRequest) { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('_loadClojureScriptCompletions cancelled', requestId, this._lastAutocompleteRequest); } return; } const sortCompletions = completions => { - return dirac.stableSort(completions, (a, b) => { + return diracAngel.stableSort(completions, (a, b) => { return a.text.localeCompare(b.text); }); }; @@ -778,12 +837,12 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { this._updateAnchorBox(); const shouldShowForSingleItem = true; // later maybe implement inline completions like in TextPrompt.js if (this._anchorBox) { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('calling SuggestBox.updateSuggestions', this._anchorBox, processedCompletions, shouldShowForSingleItem, this._userEnteredText); } this._suggestBox.updateSuggestions(this._anchorBox, processedCompletions, true, shouldShowForSingleItem, this._userEnteredText); } else { - if (dirac.DEBUG_COMPLETIONS) { + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('not calling SuggestBox.updateSuggestions because this._anchorBox is null', processedCompletions, shouldShowForSingleItem, this._userEnteredText); } } @@ -831,7 +890,8 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { * @param {boolean=} isIntermediateSuggestion */ applySuggestion(suggestion, isIntermediateSuggestion) { - if (dirac.DEBUG_COMPLETIONS) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('applySuggestion', this._lastExpression, suggestion); } const suggestionText = suggestion ? suggestion.text : ''; @@ -851,7 +911,8 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { } const selections = this._codeMirror.listSelections().slice(); - if (dirac.DEBUG_COMPLETIONS) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.DEBUG_COMPLETIONS) { console.log('acceptSuggestion', this._prefixRange, selections); } const prefixLength = this._prefixRange.endColumn - this._prefixRange.startColumn; @@ -886,6 +947,13 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { return res.join('\n'); } + /** + * @param {boolean} value + */ + setAddCompletionsFromHistory(value) { + // no op + } + /** * @param {!TextUtils.TextRange} textRange */ @@ -944,10 +1012,10 @@ export class ConsoleDiracPrompt extends UI.TextPrompt.TextPrompt { } try { - dirac.ignoreEnter = true; // a workaround for https://github.com/binaryage/dirac/issues/72 - UI.TextPrompt.TextPrompt.prototype.onKeyDown.apply(this, arguments); + this._ignoreEnter = true; // a workaround for https://github.com/binaryage/dirac/issues/72 + super.onKeyDown(event); } finally { - dirac.ignoreEnter = false; + this._ignoreEnter = false; } } } diff --git a/front_end/console/ConsoleView.js b/front_end/console/ConsoleView.js index ffd8c679510..c5986eabf86 100644 --- a/front_end/console/ConsoleView.js +++ b/front_end/console/ConsoleView.js @@ -1,4 +1,3 @@ -// @ts-nocheck /* * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2009 Joseph Pecoraro @@ -43,7 +42,8 @@ import {ConsoleFilter, FilterType} from './ConsoleFilter.js'; import {ConsolePinPane} from './ConsolePinPane.js'; import {ConsolePrompt, Events as ConsolePromptEvents} from './ConsolePrompt.js'; import {ConsoleSidebar, Events} from './ConsoleSidebar.js'; -import {ConsoleCommand, ConsoleCommandResult, ConsoleGroupViewMessage, ConsoleTableMessageView, ConsoleViewMessage, getMessageForElement, MaxLengthForLinks} from './ConsoleViewMessage.js'; // eslint-disable-line no-unused-vars +import {ConsoleCommand, ConsoleCommandResult, ConsoleDiracCommand, ConsoleDiracMarkup, ConsoleGroupViewMessage, ConsoleTableMessageView, ConsoleViewMessage, getMessageForElement, MaxLengthForLinks} from './ConsoleViewMessage.js'; // eslint-disable-line no-unused-vars + import {ConsoleViewport, ConsoleViewportElement, ConsoleViewportProvider} from './ConsoleViewport.js'; // eslint-disable-line no-unused-vars import {ConsoleDiracPrompt} from './ConsoleDiracPrompt.js'; @@ -117,6 +117,19 @@ class IssueMessage { } } +/** @typedef {{id: string, + prompt: !(ConsoleDiracPrompt|ConsolePrompt), + element: HTMLElement, + proxy: HTMLElement + status?: ?HTMLElement + statusContent?: ?HTMLElement, + statusBanner?: ?HTMLElement, + codeMirror?: ?CodeMirror, + statusBannerCallback?: ?Function}} + */ +// @ts-ignore typedef +export let ConsolePromptDescriptor; // eslint-disable-line no-unused-vars + /** * @implements {UI.SearchableView.Searchable} * @implements {ConsoleViewportProvider} @@ -132,7 +145,8 @@ export class ConsoleView extends UI.Widget.VBox { this.registerRequiredCSS('console/dirac-codemirror.css'); this.registerRequiredCSS('console/dirac-theme.css'); this.registerRequiredCSS('console/dirac-prompt.css'); - dirac.initConsole(); + const diracAngel = Common.getDiracAngel(); + diracAngel.initConsole(); this._searchableView = new UI.SearchableView.SearchableView(this); this._searchableView.element.classList.add('console-searchable-view'); @@ -308,7 +322,7 @@ export class ConsoleView extends UI.Widget.VBox { const diracPromptElement = this._messagesElement.createChild('div', 'source-code'); diracPromptElement.id = 'console-prompt-dirac'; diracPromptElement.spellcheck = false; - const diracPromptCodeMirrorInstance = dirac.adoptPrompt(diracPromptElement, dirac.hasParinfer); + const diracPromptCodeMirrorInstance = diracAngel.adoptPrompt(diracPromptElement, diracAngel.toggles.hasParinfer); diracPromptElement.classList.add('inactive-prompt'); @@ -329,6 +343,7 @@ export class ConsoleView extends UI.Widget.VBox { this._consoleHistorySetting = Common.Settings.Settings.instance().createLocalSetting('consoleHistory', []); + /** @type {ConsolePrompt|ConsoleDiracPrompt} */ this._prompt = new ConsolePrompt(); this._prompt.show(this._promptElement); this._prompt.element.addEventListener('keydown', this._promptKeyDown.bind(this), true); @@ -353,22 +368,24 @@ export class ConsoleView extends UI.Widget.VBox { /** @type {!Object.} */ this._pendingDiracCommands = {}; this._lastDiracCommandId = 1; - this._prompts = []; - this._prompts.push({id: 'js', + /** @type {!Array.} */ + this._prompts = [{ + id: 'js', prompt: this._prompt, element: this._promptElement, - proxy: this._prompt.element}); + proxy: this._prompt.element + }]; this._activePromptIndex = 0; - if (dirac.hasREPL) { + if (diracAngel.toggles.hasREPL) { const diracPrompt = new ConsoleDiracPrompt(diracPromptCodeMirrorInstance); diracPrompt.setAutocompletionTimeout(0); diracPrompt.renderAsBlock(); - const diracProxyElement = diracPrompt.attach(diracPromptElement); + const diracProxyElement = /** @type {HTMLElement}*/(diracPrompt.attach(diracPromptElement)); diracProxyElement.classList.add('console-prompt-dirac-wrapper'); diracProxyElement.addEventListener('keydown', this._promptKeyDown.bind(this), true); - this._diracHistorySetting = self.Common.settings.createLocalSetting('diracHistory', []); + this._diracHistorySetting = Common.Settings.Settings.instance().createLocalSetting('diracHistory', []); const diracHistoryData = this._diracHistorySetting.get(); diracPrompt.history().setHistoryData(diracHistoryData); @@ -380,6 +397,9 @@ export class ConsoleView extends UI.Widget.VBox { const statusContentElement = statusElement.createChild('div', 'status-content'); statusContentElement.tabIndex = 0; // focusable for page-up/down + /** @type {null|string} */ + this._currentCompiler = null; + /** @type {!ConsolePromptDescriptor}*/ this._diracPromptDescriptor = {id: 'dirac', prompt: diracPrompt, element: diracPromptElement, @@ -387,7 +407,9 @@ export class ConsoleView extends UI.Widget.VBox { status: statusElement, statusContent: statusContentElement, statusBanner: statusBannerElement, - codeMirror: diracPromptCodeMirrorInstance}; + codeMirror: diracPromptCodeMirrorInstance, + statusBannerCallback: null, + }; this._prompts.push(this._diracPromptDescriptor); } @@ -396,21 +418,21 @@ export class ConsoleView extends UI.Widget.VBox { UI.Context.Context.instance().addFlavorChangeListener( SDK.RuntimeModel.ExecutionContext, this._executionContextChanged, this); - const defaultPromptIndex = dirac.hostedInExtension ? 0 : 1; - this._consolePromptIndexSetting = self.Common.settings.createLocalSetting('consolePromptIndex', defaultPromptIndex); + const defaultPromptIndex = diracAngel.hostedInExtension ? 0 : 1; + this._consolePromptIndexSetting = Common.Settings.Settings.instance().createLocalSetting('consolePromptIndex', defaultPromptIndex); this._consoleFeedback = 0; - if (dirac.hasREPL) { + if (diracAngel.toggles.hasREPL) { this.setDiracPromptMode('status'); } else { - dirac.feedback('!dirac.hasREPL'); + diracAngel.feedback('!dirac.hasREPL'); } - dirac.feedback('ConsoleView constructed'); - if (dirac.hasWelcomeMessage) { + diracAngel.feedback('ConsoleView constructed'); + if (diracAngel.toggles.hasWelcomeMessage) { this.displayWelcomeMessage(); } else { - dirac.feedback('!dirac.hasWelcomeMessage'); + diracAngel.feedback('!dirac.hasWelcomeMessage'); } this._messagesElement.addEventListener( @@ -763,25 +785,44 @@ export class ConsoleView extends UI.Widget.VBox { * @param {!Event} event */ _diracStatusBannerClick(event) { - if (!event.target || event.target.tagName !== 'A') { + const target = /** @type {?HTMLElement} */(event.target); + if (!target || target.tagName !== 'A') { return false; } - if (this._diracPromptDescriptor.statusBannerCallback) { - this._diracPromptDescriptor.statusBannerCallback('click', event); + const statusBannerCallback = this._diracPromptDescriptor.statusBannerCallback; + if (statusBannerCallback) { + statusBannerCallback('click', event); } return false; } + /** + * @param {string} s + */ setDiracPromptStatusContent(s) { - dirac.feedback("setDiracPromptStatusContent('" + s + "')"); - this._diracPromptDescriptor.statusContent.innerHTML = s; + const diracAngel = Common.getDiracAngel(); + diracAngel.feedback("setDiracPromptStatusContent('" + s + "')"); + const statusContent = this._diracPromptDescriptor.statusContent; + if (statusContent) { + statusContent.innerHTML = s; + } } + /** + * @param {string} s + */ setDiracPromptStatusBanner(s) { - dirac.feedback("setDiracPromptStatusBanner('" + s + "')"); - this._diracPromptDescriptor.statusBanner.innerHTML = s; + const diracAngel = Common.getDiracAngel(); + diracAngel.feedback("setDiracPromptStatusBanner('" + s + "')"); + const banner = this._diracPromptDescriptor.statusBanner; + if (banner) { + banner.innerHTML = s; + } } + /** + * @param {Function | null} callback + */ setDiracPromptStatusBannerCallback(callback) { this._diracPromptDescriptor.statusBannerCallback = callback; } @@ -790,14 +831,18 @@ export class ConsoleView extends UI.Widget.VBox { * @param {string} style */ setDiracPromptStatusStyle(style) { - dirac.feedback("setDiracPromptStatusStyle('" + style + "')"); + const diracAngel = Common.getDiracAngel(); + diracAngel.feedback("setDiracPromptStatusStyle('" + style + "')"); const knownStyles = ['error', 'info']; if (knownStyles.indexOf(style) === -1) { console.warn('unknown style passed to setDiracPromptStatusStyle:', style); } for (let i = 0; i < knownStyles.length; i++) { const s = knownStyles[i]; - this._diracPromptDescriptor.status.classList.toggle('dirac-prompt-status-' + s, style === s); + const status = this._diracPromptDescriptor.status; + if (status) { + status.classList.toggle('dirac-prompt-status-' + s, style === s); + } } } @@ -805,14 +850,18 @@ export class ConsoleView extends UI.Widget.VBox { * @param {string} mode */ setDiracPromptMode(mode) { - dirac.feedback("setDiracPromptMode('" + mode + "')"); + const diracAngel = Common.getDiracAngel(); + diracAngel.feedback("setDiracPromptMode('" + mode + "')"); const knownModes = ['edit', 'status']; if (knownModes.indexOf(mode) === -1) { console.warn('unknown mode passed to setDiracPromptMode:', mode); } for (let i = 0; i < knownModes.length; i++) { const m = knownModes[i]; - this._diracPromptDescriptor.element.classList.toggle('dirac-prompt-mode-' + m, mode === m); + const element = this._diracPromptDescriptor.element; + if (element) { + element.classList.toggle('dirac-prompt-mode-' + m, mode === m); + } } if (mode === 'edit') { this.focus(); @@ -833,9 +882,10 @@ export class ConsoleView extends UI.Widget.VBox { const compilerEl = document.createElement('span'); compilerEl.classList.add('dirac-prompt-compiler'); compilerEl.textContent = compiler; - placeholderEl.appendChildren(namespaceEl, compilerEl); + placeholderEl.appendChild(namespaceEl); + placeholderEl.appendChild(compilerEl); } else { - placeholderEl.appendChildren(namespaceEl); + placeholderEl.appendChild(namespaceEl); } return placeholderEl; } @@ -849,15 +899,21 @@ export class ConsoleView extends UI.Widget.VBox { const namespace = this._currentNamespace || ''; const compiler = this._currentCompiler; const placeholderEl = this._buildPromptPlaceholder(namespace, compiler); - const cm = promptDescriptor.codeMirror; - // code mirror won't switch the placeholder if the input has focus - const hadFocus = cm.hasFocus(); - if (hadFocus) { - cm.display.input.blur(); - } - promptDescriptor.codeMirror.setOption('placeholder', placeholderEl); - if (hadFocus) { - cm.focus(); + const codeMirror = promptDescriptor.codeMirror; + if (codeMirror) { + // code mirror won't switch the placeholder if the input has focus + // @ts-ignore + const hadFocus = codeMirror.hasFocus(); + if (hadFocus) { + // @ts-ignore + codeMirror.display.input.blur(); + } + // @ts-ignore + codeMirror.setOption('placeholder', placeholderEl); + if (hadFocus) { + // @ts-ignore + codeMirror.focus(); + } } } @@ -865,10 +921,12 @@ export class ConsoleView extends UI.Widget.VBox { * @param {string} name */ setDiracPromptNS(name) { - dirac.feedback("setDiracPromptNS('" + name + "')"); + const diracAngel = Common.getDiracAngel(); + diracAngel.feedback("setDiracPromptNS('" + name + "')"); this._currentNamespace = name; if (this._diracPromptDescriptor) { - this._diracPromptDescriptor.prompt.setCurrentClojureScriptNamespace(name); + const diracPrompt = /** @type {ConsoleDiracPrompt} */(this._diracPromptDescriptor.prompt); + diracPrompt.setCurrentClojureScriptNamespace(name); } this._refreshPromptInfo(); } @@ -877,7 +935,7 @@ export class ConsoleView extends UI.Widget.VBox { * @param {string} name */ setDiracPromptCompiler(name) { - // dirac.feedback("setDiracPromptCompiler('"+name+"')"); + // diracAngel.feedback("setDiracPromptCompiler('"+name+"')"); this._currentCompiler = name; this._refreshPromptInfo(); } @@ -886,15 +944,17 @@ export class ConsoleView extends UI.Widget.VBox { * @param {number} _requestId */ onJobStarted(_requestId) { - dirac.feedback('repl eval job started'); + const diracAngel = Common.getDiracAngel(); + diracAngel.feedback('repl eval job started'); } /** * @param {number} requestId */ onJobEnded(requestId) { + const diracAngel = Common.getDiracAngel(); delete this._pendingDiracCommands[requestId]; - dirac.feedback('repl eval job ended'); + diracAngel.feedback('repl eval job ended'); } /** @@ -902,7 +962,11 @@ export class ConsoleView extends UI.Widget.VBox { */ getSuggestBoxRepresentation() { const promptDescriptor = this.getCurrentPromptDescriptor(); - return promptDescriptor.id + ' prompt: ' + promptDescriptor.prompt.getSuggestBoxRepresentation(); + if (promptDescriptor === this._diracPromptDescriptor) { + const diracPrompt = /** @type {ConsoleDiracPrompt} */(promptDescriptor.prompt); + return promptDescriptor.id + ' prompt: ' + diracPrompt.getSuggestBoxRepresentation(); + } + return promptDescriptor.id + ' prompt: ' + 'not implemented'; } /** @@ -928,9 +992,10 @@ export class ConsoleView extends UI.Widget.VBox { handleEvalJSConsoleDiracMessage(message) { const code = message.parameters[2]; if (code && typeof code.value === 'string') { - const jsPromptDescriptor = this._getPromptDescriptor('js'); + const jsPromptDescriptor = /** @type {!ConsolePromptDescriptor} */(this._getPromptDescriptor('js')); if (jsPromptDescriptor) { - jsPromptDescriptor.prompt._appendCommand(code.value, true); + const jsPrompt = /** @type {ConsolePrompt} */(jsPromptDescriptor.prompt); + jsPrompt._appendCommand(code.value, true); } } } @@ -963,8 +1028,6 @@ export class ConsoleView extends UI.Widget.VBox { * @return {?string} */ _alterDiracViewMessage(message) { - const nestingLevel = this._currentGroup.nestingLevel(); - message.messageText = ''; if (message.parameters) { message.parameters.shift(); // "~~$DIRAC-LOG$~~" @@ -978,14 +1041,18 @@ export class ConsoleView extends UI.Widget.VBox { let kind = ''; try { if (message.parameters) { - requestId = /** @type {number} */(message.parameters.shift().value); // request-id - kind = /** @type {string} */(message.parameters.shift().value); + const first = /** @type {{value:number}}*/(message.parameters.shift()); + const second = /** @type {{value:string}}*/(message.parameters.shift()); + if (first && second) { + requestId = first.value; // request-id + kind = second.value; + } } } catch (e) { } if (kind === 'result') { - message.type = SDK.ConsoleModel.MessageType.Result; + message._type = SDK.ConsoleModel.MessageType.Result; } const originatingMessage = this._pendingDiracCommands[requestId]; @@ -1050,7 +1117,8 @@ export class ConsoleView extends UI.Widget.VBox { if (consoleMessageTextEl) { const messageText = consoleMessageTextEl.deepTextContent(); const glue = (messageText.indexOf('\n') === -1) ? '> ' : '>\n'; // log multi-line log messages on a new line - dirac.feedback(typeText + '.' + levelText + glue + messageText); + const diracAngel = Common.getDiracAngel(); + diracAngel.feedback(typeText + '.' + levelText + glue + messageText); } } @@ -1062,24 +1130,27 @@ export class ConsoleView extends UI.Widget.VBox { * @return {boolean} */ appendDiracMarkup(markup) { - const target = self.SDK.targetManager.mainTarget(); + const targetManager = SDK.SDKModel.TargetManager.instance(); + const target = targetManager.mainTarget(); if (!target) { return false; } - const runtimeModel = target.model(self.SDK.RuntimeModel); + const runtimeModel = target.model(SDK.RuntimeModel.RuntimeModel); if (!runtimeModel) { return false; } const source = SDK.ConsoleModel.MessageSource.Other; const level = SDK.ConsoleModel.MessageLevel.Info; const type = SDK.ConsoleModel.MessageType.DiracMarkup; - const message = new self.SDK.ConsoleMessage(runtimeModel, source, level, markup, type); - self.SDK.consoleModel.addMessage(message); + const message = new SDK.ConsoleModel.ConsoleMessage(runtimeModel, source, level, markup, type); + const consoleModel = SDK.ConsoleModel.ConsoleModel.instance(); + consoleModel.addMessage(message); return true; } displayWelcomeMessage() { - dirac.feedback('displayWelcomeMessage'); + const diracAngel = Common.getDiracAngel(); + diracAngel.feedback('displayWelcomeMessage'); /** * @param {string} text */ @@ -1094,7 +1165,7 @@ export class ConsoleView extends UI.Widget.VBox { }; const welcomeMessage = - 'Welcome to ' + wrapBold('Dirac DevTools v' + dirac.getVersion()) + '.' + + 'Welcome to ' + wrapBold('Dirac DevTools v' + diracAngel.getVersion()) + '.' + ' Cycle CLJS/JS prompts with ' + wrapCode('CTRL+,') + '.' + ' Enter ' + wrapCode('dirac') + ' for additional info.'; @@ -1146,12 +1217,13 @@ export class ConsoleView extends UI.Widget.VBox { oldPromptDescriptor.element.classList.add('inactive-prompt'); - dirac.feedback("switched console prompt to '" + newPromptDescriptor.id + "'"); + const diracAngel = Common.getDiracAngel(); + diracAngel.feedback("switched console prompt to '" + newPromptDescriptor.id + "'"); this._prompt.setText(''); // clear prompt when switching this.focus(); if (newPromptDescriptor.id === 'dirac') { - dirac.initRepl(); + diracAngel.initRepl(); } } @@ -1200,7 +1272,7 @@ export class ConsoleView extends UI.Widget.VBox { } /** - * @return {!Object} + * @return {!ConsolePromptDescriptor} */ getCurrentPromptDescriptor() { return this._prompts[this._activePromptIndex]; @@ -1213,6 +1285,7 @@ export class ConsoleView extends UI.Widget.VBox { const promptDescriptor = this.getCurrentPromptDescriptor(); let inputEl = promptDescriptor.proxy; if (promptDescriptor.codeMirror) { + // @ts-ignore inputEl = promptDescriptor.codeMirror.getInputField(); } return inputEl; @@ -1225,7 +1298,8 @@ export class ConsoleView extends UI.Widget.VBox { dispatchEventsForPromptInput(input) { return new Promise(resolve => { const continuation = () => resolve("entered input: '" + input + "'"); - const keyboard = Keysim.Keyboard.US_ENGLISH; + const diracAngel = Common.getDiracAngel(); + const keyboard = diracAngel.Keysim.Keyboard.US_ENGLISH; keyboard.dispatchEventsForInput(input, this.getTargetForPromptEvents(), continuation); }); } @@ -1237,7 +1311,8 @@ export class ConsoleView extends UI.Widget.VBox { dispatchEventsForPromptAction(action) { return new Promise(resolve => { const continuation = () => resolve("performed action: '" + action + "'"); - const keyboard = Keysim.Keyboard.US_ENGLISH; + const diracAngel = Common.getDiracAngel(); + const keyboard = diracAngel.Keysim.Keyboard.US_ENGLISH; keyboard.dispatchEventsForAction(action, this.getTargetForPromptEvents(), continuation); }); } @@ -1263,8 +1338,9 @@ export class ConsoleView extends UI.Widget.VBox { * @param {?number} id */ appendDiracCommand(text, id) { - if (!text) - {return;} + if (!text) { + return; + } if (!id) { id = this._lastDiracCommandId++; @@ -1273,7 +1349,8 @@ export class ConsoleView extends UI.Widget.VBox { const command = text; const commandId = id; - const executionContext = self.UI.context.flavor(self.SDK.ExecutionContext); + const context = UI.Context.Context.instance(); + const executionContext = context.flavor(SDK.RuntimeModel.ExecutionContext); if (!executionContext) { return; } @@ -1283,22 +1360,26 @@ export class ConsoleView extends UI.Widget.VBox { const type = SDK.ConsoleModel.MessageType.DiracCommand; const source = SDK.ConsoleModel.MessageSource.JS; const level = SDK.ConsoleModel.MessageLevel.Info; - const commandMessage = new self.SDK.ConsoleMessage(runtimeModel, source, level, text, type); + const commandMessage = new SDK.ConsoleModel.ConsoleMessage(runtimeModel, source, level, text, type); commandMessage.setExecutionContextId(executionContext.id); - self.SDK.consoleModel.addMessage(commandMessage); + const consoleModel = SDK.ConsoleModel.ConsoleModel.instance(); + consoleModel.addMessage(commandMessage); this._prompt.history().pushHistoryItem(text); - this._diracHistorySetting.set(this._prompt.history().historyData().slice(-persistedHistorySize)); + if (this._diracHistorySetting) { + this._diracHistorySetting.set(this._prompt.history().historyData().slice(-persistedHistorySize)); + } const debuggerModel = executionContext.debuggerModel; let scopeInfoPromise = Promise.resolve(null); + const diracAngel = Common.getDiracAngel(); if (debuggerModel) { - scopeInfoPromise = dirac.extractScopeInfoFromScopeChainAsync(debuggerModel.selectedCallFrame()); + scopeInfoPromise = diracAngel.extractScopeInfoFromScopeChainAsync(debuggerModel.selectedCallFrame()); } this._pendingDiracCommands[commandId] = commandMessage; scopeInfoPromise.then(function(scopeInfo) { - dirac.sendEvalRequest(commandId, command, scopeInfo); + diracAngel.sendEvalRequest(commandId, command, scopeInfo); }); } @@ -1314,7 +1395,11 @@ export class ConsoleView extends UI.Widget.VBox { * @param {!SDK.ConsoleModel.ConsoleMessage} message */ _normalizeMessageTimestamp(message) { - message.timestamp = this._consoleMessages.length ? this._consoleMessages.peekLast().consoleMessage().timestamp : 0; + message.timestamp = 0; + const last = this._consoleMessages.peekLast(); + if (last) { + last.consoleMessage().timestamp; + } } /** @@ -1788,16 +1873,18 @@ export class ConsoleView extends UI.Widget.VBox { UI.KeyboardShortcut.KeyboardShortcut.makeKey('u', UI.KeyboardShortcut.Modifiers.Ctrl), this._clearPromptBackwards.bind(this)); - const section = self.UI.shortcutsScreen.section(Common.UIString.UIString('Console')); + // const section = self.UI.shortcutsScreen.section(Common.UIString.UIString('Console')); const shortcut = UI.KeyboardShortcut.KeyboardShortcut; - if (dirac.hasREPL) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.hasREPL) { const keys = [ shortcut.makeDescriptor(UI.KeyboardShortcut.Keys.Comma, UI.KeyboardShortcut.Modifiers.Ctrl), shortcut.makeDescriptor(UI.KeyboardShortcut.Keys.Period, UI.KeyboardShortcut.Modifiers.Ctrl) ]; - this._shortcuts[keys[0].key] = this._selectNextPrompt.bind(this); - this._shortcuts[keys[1].key] = this._selectPrevPrompt.bind(this); - section.addRelatedKeys(keys, Common.UIString.UIString('Next/previous prompt')); + this._shortcuts.set(keys[0].key, this._selectNextPrompt.bind(this)); + this._shortcuts.set(keys[1].key, this._selectPrevPrompt.bind(this)); + // TODO: dirac + // section.addRelatedKeys(keys, Common.UIString.UIString('Next/previous prompt')); } } @@ -1818,8 +1905,9 @@ export class ConsoleView extends UI.Widget.VBox { // let's wait for upstream to finish transition to ConsolePrompt.js const promptDescriptor = this._prompts[this._activePromptIndex]; if (promptDescriptor.id === 'dirac') { - if (event.altKey || event.ctrlKey || event.shiftKey) - {return;} + if (keyboardEvent.altKey || keyboardEvent.ctrlKey || keyboardEvent.shiftKey) { + return; + } event.consume(true); @@ -2137,19 +2225,19 @@ export class ConsoleViewFilter { this._messageLevelFiltersSetting = ConsoleViewFilter.levelFilterSetting(); this._hideNetworkMessagesSetting = Common.Settings.Settings.instance().moduleSetting('hideNetworkMessages'); this._filterByExecutionContextSetting = - Common.Settings.Settings.instance().moduleSetting('selectedContextFilterEnabled'); + Common.Settings.Settings.instance().moduleSetting('selectedContextFilterEnabled'); this._messageLevelFiltersSetting.addChangeListener(this._onFilterChanged.bind(this)); this._hideNetworkMessagesSetting.addChangeListener(this._onFilterChanged.bind(this)); this._filterByExecutionContextSetting.addChangeListener(this._onFilterChanged.bind(this)); UI.Context.Context.instance().addFlavorChangeListener( - SDK.RuntimeModel.ExecutionContext, this._onFilterChanged, this); + SDK.RuntimeModel.ExecutionContext, this._onFilterChanged, this); const filterKeys = Object.values(FilterType); this._suggestionBuilder = new UI.FilterSuggestionBuilder.FilterSuggestionBuilder(filterKeys); this._textFilterUI = new UI.Toolbar.ToolbarInput( - Common.UIString.UIString('Filter'), '', 0.2, 1, Common.UIString.UIString('e.g. /event\\d/ -cdn url:a.com'), - this._suggestionBuilder.completions.bind(this._suggestionBuilder)); + Common.UIString.UIString('Filter'), '', 0.2, 1, Common.UIString.UIString('e.g. /event\\d/ -cdn url:a.com'), + this._suggestionBuilder.completions.bind(this._suggestionBuilder)); this._textFilterSetting = Common.Settings.Settings.instance().createSetting('console.textFilter', ''); if (this._textFilterSetting.get()) { this._textFilterUI.setValue(this._textFilterSetting.get()); @@ -2173,7 +2261,7 @@ export class ConsoleViewFilter { this._levelMenuButton = new UI.Toolbar.ToolbarButton(ls`Log levels`); this._levelMenuButton.turnIntoSelect(); this._levelMenuButton.addEventListener( - UI.Toolbar.ToolbarButton.Events.Click, this._showLevelContextMenu.bind(this)); + UI.Toolbar.ToolbarButton.Events.Click, this._showLevelContextMenu.bind(this)); UI.ARIAUtils.markAsMenuButton(this._levelMenuButton.element); this._updateLevelMenuButtonText(); @@ -2185,7 +2273,7 @@ export class ConsoleViewFilter { */ onMessageAdded(message) { if (message.type === SDK.ConsoleModel.MessageType.Command || message.type === SDK.ConsoleModel.MessageType.Result || - message.isGroupMessage()) { + message.isGroupMessage()) { return; } if (message.context) { @@ -2204,19 +2292,19 @@ export class ConsoleViewFilter { */ static levelFilterSetting() { return Common.Settings.Settings.instance().createSetting( - 'messageLevelFilters', ConsoleFilter.defaultLevelsFilterValue()); + 'messageLevelFilters', ConsoleFilter.defaultLevelsFilterValue()); } _updateCurrentFilter() { const parsedFilters = this._filterParser.parse(this._textFilterUI.value()); if (this._hideNetworkMessagesSetting.get()) { parsedFilters.push( - {key: FilterType.Source, text: SDK.ConsoleModel.MessageSource.Network, negative: true, regex: undefined}); + {key: FilterType.Source, text: SDK.ConsoleModel.MessageSource.Network, negative: true, regex: undefined}); } this._currentFilter.executionContext = this._filterByExecutionContextSetting.get() ? - UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext) : - null; + UI.Context.Context.instance().flavor(SDK.RuntimeModel.ExecutionContext) : + null; this._currentFilter.parsedFilters = parsedFilters; this._currentFilter.levelsMask = this._messageLevelFiltersSetting.get(); } @@ -2239,7 +2327,7 @@ export class ConsoleViewFilter { isDefault = isDefault && levels[name] === defaultValue[name]; if (levels[name]) { text = text ? Common.UIString.UIString('Custom levels') : - Common.UIString.UIString('%s only', this._levelLabels.get(name)); + Common.UIString.UIString('%s only', this._levelLabels.get(name)); } } if (isAll) { @@ -2263,11 +2351,11 @@ export class ConsoleViewFilter { const levels = setting.get(); const contextMenu = new UI.ContextMenu.ContextMenu( - mouseEvent, true /* useSoftMenu */, this._levelMenuButton.element.totalOffsetLeft(), - this._levelMenuButton.element.totalOffsetTop() + - /** @type {!HTMLElement} */ (this._levelMenuButton.element).offsetHeight); + mouseEvent, true /* useSoftMenu */, this._levelMenuButton.element.totalOffsetLeft(), + this._levelMenuButton.element.totalOffsetTop() + + /** @type {!HTMLElement} */ (this._levelMenuButton.element).offsetHeight); contextMenu.headerSection().appendItem( - Common.UIString.UIString('Default'), () => setting.set(ConsoleFilter.defaultLevelsFilterValue())); + Common.UIString.UIString('Default'), () => setting.set(ConsoleFilter.defaultLevelsFilterValue())); for (const [level, levelText] of this._levelLabels.entries()) { contextMenu.defaultSection().appendCheckboxItem(levelText, toggleShowLevel.bind(null, level), levels[level]); } @@ -2316,132 +2404,6 @@ export class ConsoleViewFilter { } } -export class ConsoleCommand extends ConsoleViewMessage { - /** - * @param {!SDK.ConsoleModel.ConsoleMessage} consoleMessage - * @param {!Components.Linkifier.Linkifier} linkifier - * @param {number} nestingLevel - * @param {function(!Common.EventTarget.EventTargetEvent):void} onResize - */ - constructor(consoleMessage, linkifier, nestingLevel, onResize) { - super(consoleMessage, linkifier, nestingLevel, onResize); - /** @type {?HTMLElement} */ - this._formattedCommand = null; - } - - /** - * @override - * @return {!HTMLElement} - */ - contentElement() { - const contentElement = this.getContentElement(); - if (contentElement) { - return contentElement; - } - const newContentElement = /** @type {!HTMLElement} */ (document.createElement('div')); - this.setContentElement(newContentElement); - newContentElement.classList.add('console-user-command'); - const icon = UI.Icon.Icon.create('smallicon-user-command', 'command-result-icon'); - newContentElement.appendChild(icon); - - // ts-expect-error We can't convert this to a Weakmap, as it comes from `ConsoleViewMessage` instead. - newContentElement.message = this; - - this._formattedCommand = /** @type {!HTMLElement} */ (document.createElement('span')); - this._formattedCommand.classList.add('source-code'); - this._formattedCommand.textContent = Platform.StringUtilities.replaceControlCharacters(this.text); - newContentElement.appendChild(this._formattedCommand); - - if (this._formattedCommand.textContent.length < MaxLengthToIgnoreHighlighter) { - const javascriptSyntaxHighlighter = new UI.SyntaxHighlighter.SyntaxHighlighter('text/javascript', true); - javascriptSyntaxHighlighter.syntaxHighlightNode(this._formattedCommand).then(this._updateSearch.bind(this)); - } else { - this._updateSearch(); - } - - this.updateTimestamp(); - return newContentElement; - } - - _updateSearch() { - this.setSearchRegex(this.searchRegex()); - } -} - -/** - * @unrestricted - */ -class ConsoleDiracCommand extends ConsoleCommand { - /** - * @override - * @return {!Element} - */ - contentElement() { - if (!this._contentElement) { - this._contentElement = document.createElement('div'); - this._contentElement.classList.add('console-user-command'); - this._contentElement.message = this; - const icon = UI.Icon.Icon.create('smallicon-user-command', 'command-result-icon'); - this._contentElement.appendChild(icon); - - this._formattedCommand = document.createElement('span'); - this._formattedCommand.classList.add('console-message-text', 'source-code', 'cm-s-dirac'); - this._contentElement.appendChild(this._formattedCommand); - - CodeMirror.runMode(this.text, 'clojure-parinfer', this._formattedCommand, undefined); - - this.element().classList.add('dirac-flavor'); // applied to wrapper element - } - return this._contentElement; - } -} - -/** - * @unrestricted - */ -class ConsoleDiracMarkup extends ConsoleCommand { - /** - * @override - * @return {!Element} - */ - contentElement() { - if (!this._contentElement) { - this._contentElement = document.createElement('div'); - this._contentElement.classList.add('console-message', 'console-dirac-markup'); - this._contentElement.message = this; - - this._formattedCommand = document.createElement('span'); - this._formattedCommand.classList.add('console-message-text', 'source-code'); - this._formattedCommand.innerHTML = this.consoleMessage().messageText; - this._contentElement.appendChild(this._formattedCommand); - - this.element().classList.add('dirac-flavor'); // applied to wrapper element - } - return this._contentElement; - } -} - -/** - * @unrestricted - */ -class ConsoleCommandResult extends ConsoleViewMessage { - /** - * @override - * @return {!HTMLElement} - */ - contentElement() { - const element = super.contentElement(); - if (!element.classList.contains('console-user-command-result')) { - element.classList.add('console-user-command-result'); - if (this.consoleMessage().level === SDK.ConsoleModel.MessageLevel.Info) { - const icon = UI.Icon.Icon.create('smallicon-command-result', 'command-result-icon'); - element.insertBefore(icon, element.firstChild); - } - } - return element; - } -} - /** * @unrestricted */ @@ -2525,5 +2487,5 @@ const consoleMessageToViewMessage = new WeakMap(); /** * @typedef {{messageIndex: number, matchIndex: number}} */ -// ts-expect-error typedef +// @ts-ignore export let RegexMatchRange; diff --git a/front_end/console/ConsoleViewMessage.js b/front_end/console/ConsoleViewMessage.js index 470ed61627f..0ab4c683eaf 100644 --- a/front_end/console/ConsoleViewMessage.js +++ b/front_end/console/ConsoleViewMessage.js @@ -881,10 +881,17 @@ export class ConsoleViewMessage { } } + /** + * @param {!SDK.RemoteObject.RemoteObject|string|{description:string}|undefined} obj + */ function rawFormatter(obj) { - const rawElement = createElement('div'); + const rawElement = document.createElement('div'); rawElement.setAttribute('class', 'raw-console-output'); - rawElement.innerHTML = obj.description || ''; + // @ts-ignore + if (obj && obj.description) { + // @ts-ignore + rawElement.innerHTML = obj.description; + } return rawElement; } @@ -979,6 +986,7 @@ export class ConsoleViewMessage { } // Platform.StringUtilities.format does treat formattedResult like a Builder, result is an object. + // @ts-ignore return Platform.StringUtilities.format(format, parameters, formatters, formattedResult, append.bind(this)); } @@ -1927,6 +1935,69 @@ export class ConsoleCommandResult extends ConsoleViewMessage { } } + +/** + * @unrestricted + */ +export class ConsoleDiracCommand extends ConsoleCommand { + /** + * @override + * @return {!HTMLElement} + */ + contentElement() { + const element = super.contentElement(); + if (!element.classList.contains('console-dirac-command')) { + element.classList.add('console-user-command', 'console-dirac-command'); + const icon = UI.Icon.Icon.create('smallicon-user-command', 'command-result-icon'); + element.insertBefore(icon, element.firstChild); + + const superContent = element.querySelector('.source-code'); + if (superContent) { + superContent.remove(); + } + + this._formattedCommand = document.createElement('span'); + this._formattedCommand.classList.add('console-message-text', 'source-code', 'cm-s-dirac'); + element.appendChild(this._formattedCommand); + + // @ts-ignore + CodeMirror.runMode(this.text, 'clojure-parinfer', this._formattedCommand, undefined); + + this.element().classList.add('dirac-flavor'); // applied to wrapper element + } + return element; + } +} + +/** + * @unrestricted + */ +export class ConsoleDiracMarkup extends ConsoleCommand { + /** + * @override + * @return {!HTMLElement} + */ + contentElement() { + const element = super.contentElement(); + if (!element.classList.contains('console-dirac-markup')) { + element.classList.add('console-message', 'console-dirac-markup'); + + const superContent = element.querySelector('.source-code'); + if (superContent) { + superContent.remove(); + } + + this._formattedCommand = document.createElement('span'); + this._formattedCommand.classList.add('console-message-text', 'source-code'); + this._formattedCommand.innerHTML = this.consoleMessage().messageText; + element.appendChild(this._formattedCommand); + + this.element().classList.add('dirac-flavor'); // applied to wrapper element + } + return element; + } +} + export class ConsoleTableMessageView extends ConsoleViewMessage { /** * @param {!SDK.ConsoleModel.ConsoleMessage} consoleMessage diff --git a/front_end/console/module.json b/front_end/console/module.json index 619948efd75..217889fbda5 100644 --- a/front_end/console/module.json +++ b/front_end/console/module.json @@ -213,7 +213,6 @@ "dependencies": [ "components", "source_frame", - "dirac", "data_grid", "host", "object_ui", diff --git a/front_end/dirac/BUILD.gn b/front_end/dirac/BUILD.gn new file mode 100644 index 00000000000..8a8e9bd49a7 --- /dev/null +++ b/front_end/dirac/BUILD.gn @@ -0,0 +1,27 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("../../scripts/build/ninja/devtools_entrypoint.gni") +import("../../scripts/build/ninja/devtools_module.gni") + +devtools_module("dirac") { + sources = [ + "DiracAngel.js", + "keysim.js", + "parinfer.js", + "parinfer-codemirror.js", + ] + + deps = [ + "../common:bundle", + "../sdk:bundle", + "../root:bundle", + "../ui:bundle", + ]} + +devtools_entrypoint("bundle") { + entrypoint = "dirac.js" + + deps = [ ":dirac" ] +} diff --git a/front_end/dirac/DiracAngel.js b/front_end/dirac/DiracAngel.js new file mode 100644 index 00000000000..3fd95e9d0f4 --- /dev/null +++ b/front_end/dirac/DiracAngel.js @@ -0,0 +1,1317 @@ +// @ts-nocheck +/* eslint-disable rulesdir/check_license_header,no-console */ + +import * as Root from '../root/root.js'; +import * as UI from '../ui/ui.js'; +import * as SDKModule from '../sdk/sdk.js'; + +import * as Keysim from './keysim.js'; +import './parinfer.js'; +import './parinfer-codemirror.js'; + +console.log('dirac module import!'); + +const REMOTE_OBJECT_PROPERTIES_FETCH_TIMEOUT = 1000; + +/** @type {!Object.} */ +const featureFlags = {}; + +// WARNING: keep this in sync with background.tools/flag-keys +const knownFeatureFlags = [ + 'enable-repl', + 'enable-parinfer', + 'enable-friendly-locals', + 'enable-clustered-locals', + 'inline-custom-formatters', + 'welcome-message', + 'clean-urls', + 'beautify-function-names', + 'link-actions' +]; + +// we use can_dock url param indicator if we are launched as internal devtools +export const hostedInExtension = !Root.Runtime.Runtime.queryParam('can_dock'); + +// -- feature toggles ----------------------------------------------------------------------------------------------- + +export const toggles = { + hasREPL: hasFeature('enable-repl'), + hasParinfer: hasFeature('enable-parinfer'), + hasFriendlyLocals: hasFeature('enable-friendly-locals'), + hasClusteredLocals: hasFeature('enable-clustered-locals'), + hasInlineCFs: hasFeature('inline-custom-formatters'), + hasWelcomeMessage: hasFeature('welcome-message'), + hasCleanUrls: hasFeature('clean-urls'), + hasBeautifyFunctionNames: hasFeature('beautify-function-names'), + hasLinkActions: hasFeature('link-actions'), + + DEBUG_EVAL: hasDebugFlag('eval'), + DEBUG_COMPLETIONS: hasDebugFlag('completions'), + DEBUG_KEYSIM: hasDebugFlag('keysim'), + DEBUG_FEEDBACK: hasDebugFlag('feedback'), + DEBUG_WATCHING: hasDebugFlag('watching'), + DEBUG_CACHES: hasDebugFlag('caches'), + DEBUG_TOGGLES: hasDebugFlag('toggles'), +}; + +/** @type { Function | null} */ +let _runtimeReadyPromiseCallback = null; + +let namespacesCache = null; + +const readyPromise = new Promise(fulfil => { + _runtimeReadyPromiseCallback = fulfil; +}); + +export function getReadyPromise() { + return readyPromise; +} + +export function markAsReady() { + if (_runtimeReadyPromiseCallback) { + console.log('markAsReady!'); + _runtimeReadyPromiseCallback(); + _runtimeReadyPromiseCallback = null; + } else { + console.error('unexpected null _runtimeReadyPromiseCallback'); + throw 'unexpected null _runtimeReadyPromiseCallback'; + } +} + +/** + * @param {string} feature + */ +export function hasFeature(feature) { + const flag = featureFlags[feature]; + if (flag !== undefined) { + return flag; + } + const featureIndex = knownFeatureFlags.indexOf(feature); + if (featureIndex === -1) { + return true; + } + const activeFlags = Root.Runtime.Runtime.queryParam('dirac_flags') || ''; + const result = activeFlags[featureIndex] !== '0'; + featureFlags[feature] = result; + return result; +} + +/** + * @param {string} flagName + */ +function hasDebugFlag(flagName) { + if (Root.Runtime.Runtime.queryParam('debug_all') === '1') { + return true; + } + const paramName = 'debug_' + flagName.toLowerCase(); + return Root.Runtime.Runtime.queryParam(paramName) === '1'; +} + +/** + * @param {string} name + */ +export function getToggle(name) { + if (toggles.DEBUG_TOGGLES) { + // eslint-disable-next-line no-console + console.log('dirac: get toggle \'' + name + '\' => ' + toggles[name]); + } + return toggles[name]; +} + +/** + * @param {string} name + * @param {string} value + */ +export function setToggle(name, value) { + if (toggles.DEBUG_TOGGLES) { + // eslint-disable-next-line no-console + console.log('dirac: set toggle \'' + name + '\' => ' + value); + } + toggles[name] = value; +} + +// taken from https://github.com/joliss/js-string-escape/blob/master/index.js +/** + * @param {string} string + */ +export function stringEscape(string) { + return ('' + string).replace(/["'\\\n\r\u2028\u2029]/g, function(character) { + // Escape all characters not included in SingleStringCharacters and + // DoubleStringCharacters on + // http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4 + switch (character) { + case '"': + case '\'': + case '\\': + return '\\' + character; + // Four possible LineTerminator characters need to be escaped: + case '\n': + return '\\n'; + case '\r': + return '\\r'; + case '\u2028': + return '\\u2028'; + case '\u2029': + return '\\u2029'; + } + }); +} + +/** + * @param {string} code + */ +export function codeAsString(code) { + return '\'' + stringEscape(code) + '\''; +} + +/** + * @param {any} item + * @returns {string} + */ +function defaultDeduplicateKeyFn(item) { + return '' + item; +} + +/** + * @param {[]} coll + */ +export function deduplicate(coll, keyFn = defaultDeduplicateKeyFn) { + const store = new Set(); + return coll.filter(item => !store.has(keyFn(item)) && !!store.add(keyFn(item))); +} + +/** + * @param {[]} array + * @param {Function} comparator + */ +// http://stackoverflow.com/a/20767836/84283 +export function stableSort(array, comparator) { + const wrapped = array.map((d, i) => ({d: d, i: i})); + + wrapped.sort((a, b) => { + const cmp = comparator(a.d, b.d); + return cmp === 0 ? a.i - b.i : cmp; + }); + + return wrapped.map(wrapper => wrapper.d); +} + +export function getNamespace(namespaceName) { + if (!namespacesCache) { + return; + } + + return namespacesCache[namespaceName]; +} + +export function dispatchEventsForAction(action) { + return new Promise(resolve => { + const continuation = () => resolve('performed document action: \'' + action + '\''); + const keyboard = Keysim.Keyboard.US_ENGLISH; + keyboard.dispatchEventsForAction(action, globalThis.document, continuation); + }); +} + +/** + * @suppressGlobalPropertiesCheck + **/ +function collectShadowRoots(root = null) { + const res = []; + const startNode = root || document.body; + for (let node = startNode; node; node = node.traverseNextNode(startNode)) { + if (node instanceof ShadowRoot) { + res.push(node); + } + } + return res; +} + +export function querySelectorAllDeep(node, query) { + const roots = [node].concat(collectShadowRoots(node)); + let res = []; + for (const node of roots) { + const partial = node.querySelectorAll(query); + res = res.concat(Array.from(partial)); + } + return res; +} + +const namespacesSymbolsCache = new Map(); + +// --- eval support ----------------------------------------------------------------------------------------------------- + +export function lookupCurrentContext() { + const context = UI.Context.Context.instance(); + return context.flavor(SDKModule.RuntimeModel.ExecutionContext); +} + +export function evalInContext(context, code, silent, callback) { + if (!context) { + console.warn('Requested evalInContext with null context:', code); + return; + } + const resultCallback = function(result, exceptionDetails) { + if (toggles.DEBUG_EVAL) { + console.log('evalInContext/resultCallback: result', result, 'exceptionDetails', exceptionDetails); + } + if (callback) { + let exceptionDescription = null; + if (exceptionDetails) { + const exception = exceptionDetails.exception; + if (exception) { + exceptionDescription = exception.description; + } + if (!exceptionDescription) { + exceptionDescription = exceptionDetails.text; + } + if (!exceptionDescription) { + exceptionDescription = '?'; + } + } + + callback(result, exceptionDescription); + } + }; + try { + if (toggles.DEBUG_EVAL) { + console.log('evalInContext', context, silent, code); + } + context.evaluate({ + expression: code, + objectGroup: 'console', + includeCommandLineAPI: true, + silent: silent, + returnByValue: true, + generatePreview: false + }, false, false).then(answer => resultCallback(answer.object, answer.exceptionDetails)); + } catch (e) { + console.error('failed js evaluation in context:', context, 'code', code); + } +} + +export function hasCurrentContext() { + return !!lookupCurrentContext(); +} + +export function evalInCurrentContext(code, silent, callback) { + if (toggles.DEBUG_EVAL) { + console.log('evalInCurrentContext called:', code, silent, callback); + } + evalInContext(lookupCurrentContext(), code, silent, callback); +} + +function lookupDefaultContext() { + if (toggles.DEBUG_EVAL) { + console.log('lookupDefaultContext called'); + } + if (!SDK.targetManager) { + if (toggles.DEBUG_EVAL) { + console.log(' !SDK.targetManager => bail out'); + } + return null; + } + const target = SDK.targetManager.mainTarget(); + if (!target) { + if (toggles.DEBUG_EVAL) { + console.log(' !target => bail out'); + } + return null; + } + const runtimeModel = target.model(SDK.RuntimeModel); + if (!runtimeModel) { + if (toggles.DEBUG_EVAL) { + console.log(' !runtimeModel => bail out'); + } + return null; + } + const executionContexts = runtimeModel.executionContexts(); + if (toggles.DEBUG_EVAL) { + console.log(' execution contexts:', executionContexts); + } + for (let i = 0; i < executionContexts.length; ++i) { + const executionContext = executionContexts[i]; + if (executionContext.isDefault) { + if (toggles.DEBUG_EVAL) { + console.log(' execution context #' + i + ' isDefault:', executionContext); + } + return executionContext; + } + } + if (executionContexts.length > 0) { + if (toggles.DEBUG_EVAL) { + console.log(' lookupDefaultContext failed to find valid context => return the first one'); + } + return executionContexts[0]; + } + if (toggles.DEBUG_EVAL) { + console.log(' lookupDefaultContext failed to find valid context => no context avail'); + } + return null; +} + +export function hasDefaultContext() { + return !!lookupDefaultContext(); +} + +export function evalInDefaultContext(code, silent, callback) { + if (toggles.DEBUG_EVAL) { + console.log('evalInDefaultContext called:', code, silent, callback); + } + evalInContext(lookupDefaultContext(), code, silent, callback); +} + +export function getMainDebuggerModel() { + return SDK.targetManager.mainTarget().model(SDK.DebuggerModel); +} + +const debuggerEventsUnsubscribers = new Map(); + +/** + * @return {boolean} + */ +export function subscribeDebuggerEvents(callback) { + if (debuggerEventsUnsubscribers.has(callback)) { + throw new Error('subscribeDebuggerEvents called without prior unsubscribeDebuggerEvents for callback ' + callback); + } + const globalObjectClearedHandler = (...args) => { + callback('GlobalObjectCleared', ...args); + }; + const debuggerPausedHandler = (...args) => { + callback('DebuggerPaused', ...args); + }; + const debuggerResumedHandler = (...args) => { + callback('DebuggerResumed', ...args); + }; + + SDK.targetManager.addModelListener(SDK.DebuggerModel, SDK.DebuggerModel.Events.GlobalObjectCleared, globalObjectClearedHandler, dirac); + SDK.targetManager.addModelListener(SDK.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, debuggerPausedHandler, dirac); + SDK.targetManager.addModelListener(SDK.DebuggerModel, SDK.DebuggerModel.Events.DebuggerResumed, debuggerResumedHandler, dirac); + + debuggerEventsUnsubscribers.set(callback, () => { + SDK.targetManager.removeModelListener(SDK.DebuggerModel, SDK.DebuggerModel.Events.GlobalObjectCleared, globalObjectClearedHandler, dirac); + SDK.targetManager.removeModelListener(SDK.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, debuggerPausedHandler, dirac); + SDK.targetManager.removeModelListener(SDK.DebuggerModel, SDK.DebuggerModel.Events.DebuggerResumed, debuggerResumedHandler, dirac); + return true; + }); + + return true; +} + +/** + * @return {boolean} + */ +export function unsubscribeDebuggerEvents(callback) { + if (!debuggerEventsUnsubscribers.has(callback)) { + throw new Error('unsubscribeDebuggerEvents called without prior subscribeDebuggerEvents for callback ' + callback); + } + + const unsubscriber = debuggerEventsUnsubscribers.get(callback); + debuggerEventsUnsubscribers.delete(callback); + return unsubscriber(); +} + +// --- console ---------------------------------------------------------------------------------------------------------- + +export function addConsoleMessageToMainTarget(type, level, text, parameters) { + const target = SDK.targetManager.mainTarget(); + if (!target) { + console.warn('Unable to add console message to main target (no target): ', text); + return; + } + const runtimeModel = target.model(SDK.RuntimeModel); + if (!runtimeModel) { + console.warn('Unable to add console message to main target (no runtime model): ', text); + return; + } + const sanitizedText = text || ''; + const msg = new SDK.ConsoleMessage(runtimeModel, SDK.ConsoleMessage.MessageSource.Other, level, + sanitizedText, type, undefined, undefined, undefined, parameters); + SDK.consoleModel.addMessage(msg); +} + +export function evaluateCommandInConsole(contextName, code) { + const context = contextName === 'current' ? lookupCurrentContext() : lookupDefaultContext(); + if (!context) { + console.warn('evaluateCommandInConsole got null \'' + contextName + '\' context:', code); + return; + } + const commandMessage = new SDK.ConsoleMessage(context.runtimeModel, SDK.ConsoleMessage.MessageSource.JS, null, code, SDK.ConsoleMessage.MessageType.Command); + commandMessage.setExecutionContextId(context.id); + commandMessage.skipHistory = true; + SDK.consoleModel.evaluateCommandInConsole(context, commandMessage, code, false); +} + +// --- scope info ------------------------------------------------------------------------------------------------------- + +function getScopeTitle(scope) { + let title = null; + let scopeName = null; + + switch (scope.type()) { + case Protocol.Debugger.ScopeType.Local: + title = Common.UIString('Local'); + break; + case Protocol.Debugger.ScopeType.Closure: + scopeName = scope.name(); + if (scopeName) { + title = Common.UIString('Closure (%s)', UI.beautifyFunctionName(scopeName)); + } else { + title = Common.UIString('Closure'); + } + break; + case Protocol.Debugger.ScopeType.Catch: + title = Common.UIString('Catch'); + break; + case Protocol.Debugger.ScopeType.Block: + title = Common.UIString('Block'); + break; + case Protocol.Debugger.ScopeType.Script: + title = Common.UIString('Script'); + break; + case Protocol.Debugger.ScopeType.With: + title = Common.UIString('With Block'); + break; + case Protocol.Debugger.ScopeType.Global: + title = Common.UIString('Global'); + break; + } + + return title; +} + +function extractNamesFromScopePromise(scope) { + const title = getScopeTitle(scope); + const remoteObject = Sources.SourceMapNamesResolver.resolveScopeInObject(scope); + + const result = {title: title}; + let resolved = false; + + return new Promise(function(resolve) { + + function processProperties(answer) { + const properties = answer.properties; + if (properties) { + result.props = properties.map(function(property) { + const propertyRecord = {name: property.name}; + if (property.resolutionSourceProperty) { + const identifier = property.resolutionSourceProperty.name; + if (identifier !== property.name) { + propertyRecord.identifier = identifier; + } + } + return propertyRecord; + }); + } + + resolved = true; + resolve(result); + } + + function timeoutProperties() { + if (resolved) { + return; + } + console.warn('Unable to retrieve properties from remote object', remoteObject); + resolve(result); + } + + remoteObject.getAllProperties(false, false).then(processProperties); + setTimeout(timeoutProperties, REMOTE_OBJECT_PROPERTIES_FETCH_TIMEOUT); + }); +} + +export function extractScopeInfoFromScopeChainAsync(callFrame) { + if (!callFrame) { + return Promise.resolve(null); + } + + return new Promise(function(resolve) { + const scopeNamesPromises = []; + + const scopeChain = callFrame.scopeChain(); + for (let i = 0; i < scopeChain.length; ++i) { + const scope = scopeChain[i]; + if (scope.type() === Protocol.Debugger.ScopeType.Global) { + continue; + } + + scopeNamesPromises.unshift(extractNamesFromScopePromise(scope)); + } + + Promise.all(scopeNamesPromises).then(function(frames) { + const result = {frames: frames}; + resolve(result); + }); + }); +} + +// --- helpers ---------------------------------------------------------------------------------------------------------- + +/** + * @param {string} namespaceName + * @return {function(string)} + */ +function prepareUrlMatcher(namespaceName) { + // shadow-cljs uses slightly different convention to output files + // for example given namespaceName 'my.cool.ns' + // standard clojurescript outputs into directory structure $some-prefix/my/cool/ns.js + // cljs files are placed under the same names + // + // shadow-cljs outputs into flat directory structure cljs-runtime/my.cool.ns.js + // but shadow-cljs maintains tree-like structure for original cljs sources, similar to standard + // + const relativeNSPathStandard = nsToRelpath(namespaceName, 'js'); + const relativeNSPathShadow = relativeNSPathStandard.replace('/', '.'); + const parser = document.createElement('a'); + return /** @suppressGlobalPropertiesCheck */ function(url) { + parser.href = url; + // console.log("URL MATCH", relativeNSPathShadow, parser.pathname); + return parser.pathname.endsWith(relativeNSPathStandard) || parser.pathname.endsWith(relativeNSPathShadow); + }; +} + +function unique(a) { + return Array.from(new Set(a)); +} + +function isRelevantSourceCode(uiSourceCode) { + return uiSourceCode.contentType().isScript() && !uiSourceCode.contentType().isFromSourceMap() && + uiSourceCode.project().type() === Workspace.projectTypes.Network; +} + +function getRelevantSourceCodes(workspace) { + return workspace.uiSourceCodes().filter(isRelevantSourceCode); +} + +// --- parsing namespaces ----------------------------------------------------------------------------------------------- + +/** + * @param {string} url + * @param {string} cljsSourceCode + * @return {!Array} + */ +function parseClojureScriptNamespaces(url, cljsSourceCode) { + if (toggles.DEBUG_CACHES) { + console.groupCollapsed('parseClojureScriptNamespaces: ' + url); + console.log(cljsSourceCode); + console.groupEnd(); + } + if (!cljsSourceCode) { + console.warn('unexpected empty source from ' + url); + return []; + } + const descriptor = parseNsFromSource(cljsSourceCode); + if (!descriptor) { + return []; + } + + descriptor.url = url; + return [descriptor]; +} + +/** + * @param {string} url + * @param {?string} jsSourceCode + * @return {!Array} + */ +function parsePseudoNamespaces(url, jsSourceCode) { + if (toggles.DEBUG_CACHES) { + console.groupCollapsed('parsePseudoNamespaces: ' + url); + console.log(jsSourceCode); + console.groupEnd(); + } + if (!jsSourceCode) { + console.warn('unexpected empty source from ' + url); + return []; + } + + const result = []; + // standard clojurescript emits: goog.provide('goog.something'); + // shadow-cljs emits: goog.module("goog.something"); + const re = /goog\.(provide|module)\(['"](.*?)['"]\);/gm; + let m; + // eslint-disable-next-line no-cond-assign + while (m = re.exec(jsSourceCode)) { + const namespaceName = m[2]; + const descriptor = { + name: namespaceName, + url: url, + pseudo: true + }; + result.push(descriptor); + } + + return result; +} + +function ensureSourceMapLoadedAsync(script) { + if (!script.sourceMapURL) { + return Promise.resolve(null); + } + const sourceMap = Bindings.debuggerWorkspaceBinding.sourceMapForScript(script); + if (sourceMap) { + return Promise.resolve(sourceMap); + } + return new Promise(resolve => { + let counter = 0; + const interval = setInterval(() => { + const sourceMap = Bindings.debuggerWorkspaceBinding.sourceMapForScript(script); + if (sourceMap) { + clearInterval(interval); + resolve(sourceMap); + } + counter += 1; + if (counter > 100) { // 10s + clearInterval(interval); + console.warn('source map didn\'t load in time for', script); + resolve(null); + } + }, 100); + }); +} + +/** + * @param {!SDK.Script} script + * @return {!Promise>} + * @suppressGlobalPropertiesCheck + */ +function parseNamespacesDescriptorsAsync(script) { + if (script.isContentScript()) { + return Promise.resolve([]); + } + + // I assume calling maybeLoadSourceMap is no longer needed, source maps are loaded lazily when referenced + // Bindings.debuggerWorkspaceBinding.maybeLoadSourceMap(script); + return ensureSourceMapLoadedAsync(script).then(/** @suppressGlobalPropertiesCheck */sourceMap => { + const scriptUrl = script.contentURL(); + const promises = []; + let realNamespace = false; + if (sourceMap) { + for (const url of sourceMap.sourceURLs()) { + // take only .cljs or .cljc urls, make sure url params and fragments get matched properly + // examples: + // http://localhost:9977/.compiled/demo/clojure/browser/event.cljs?rel=1463085025939 + // http://localhost:9977/.compiled/demo/dirac_sample/demo.cljs?rel=1463085026941 + const parser = document.createElement('a'); + parser.href = url; + if (parser.pathname.match(/\.clj.$/)) { + const contentProvider = sourceMap.sourceContentProvider(url, Common.resourceTypes.SourceMapScript); + const namespaceDescriptorsPromise = contentProvider.requestContent().then(cljsSourceCode => parseClojureScriptNamespaces(scriptUrl, cljsSourceCode.content)); + promises.push(namespaceDescriptorsPromise); + realNamespace = true; + } + } + } + + // we are also interested in pseudo namespaces from google closure library + if (!realNamespace) { + const parser = document.createElement('a'); + parser.href = scriptUrl; + if (parser.pathname.match(/\.js$/)) { + const namespaceDescriptorsPromise = script.requestContent().then(jsSourceCode => parsePseudoNamespaces(scriptUrl, jsSourceCode.content)); + promises.push(namespaceDescriptorsPromise); + } + } + + const concatResults = results => { + return [].concat.apply([], results); + }; + + return Promise.all(promises).then(concatResults); + }); +} + +// --- namespace names -------------------------------------------------------------------------------------------------- + +export function getMacroNamespaceNames(namespaces) { + let names = []; + for (const descriptor of Object.values(namespaces)) { + if (!descriptor.detectedMacroNamespaces) { + continue; + } + names = names.concat(descriptor.detectedMacroNamespaces); + } + return deduplicate(names); +} + +function getSourceCodeNamespaceDescriptorsAsync(uiSourceCode) { + if (!uiSourceCode) { + return Promise.resolve([]); + } + const script = getScriptFromSourceCode(uiSourceCode); + if (!script) { + return Promise.resolve([]); + } + // noinspection JSCheckFunctionSignatures + return parseNamespacesDescriptorsAsync(script); +} + +function prepareNamespacesFromDescriptors(namespaceDescriptors) { + const result = {}; + for (const descriptor of namespaceDescriptors) { + result[descriptor.name] = descriptor; + } + return result; +} + +function extractNamespacesAsyncWorker() { + const workspace = Workspace.workspace; + if (!workspace) { + console.error('unable to locate Workspace when extracting all ClojureScript namespace names'); + return Promise.resolve([]); + } + + const uiSourceCodes = getRelevantSourceCodes(workspace); + const promises = []; + if (toggles.DEBUG_CACHES) { + console.log('extractNamespacesAsyncWorker initial processing of ' + uiSourceCodes.length + ' source codes'); + } + for (const uiSourceCode of uiSourceCodes) { + const namespaceDescriptorsPromise = getSourceCodeNamespaceDescriptorsAsync(uiSourceCode); + promises.push(namespaceDescriptorsPromise); + } + + const concatResults = results => { + return [].concat.apply([], results); + }; + + return Promise.all(promises).then(concatResults); +} + +let extractNamespacesAsyncInFlightPromise = null; + +export function extractNamespacesAsync() { + // extractNamespacesAsync can take some time parsing all namespaces + // it could happen that extractNamespacesAsync() is called multiple times from code-completion code + // here we cache in-flight promise to prevent that + if (extractNamespacesAsyncInFlightPromise) { + return extractNamespacesAsyncInFlightPromise; + } + + if (namespacesCache) { + return Promise.resolve(namespacesCache); + } + + namespacesCache = {}; + startListeningForWorkspaceChanges(); + + extractNamespacesAsyncInFlightPromise = extractNamespacesAsyncWorker().then(descriptors => { + const newDescriptors = prepareNamespacesFromDescriptors(descriptors); + const newDescriptorsCount = Object.keys(newDescriptors).length; + if (!namespacesCache) { + namespacesCache = {}; + } + Object.assign(namespacesCache, newDescriptors); + const allDescriptorsCount = Object.keys(namespacesCache).length; + if (toggles.DEBUG_CACHES) { + console.log('extractNamespacesAsync finished namespacesCache with ' + newDescriptorsCount + ' items ' + + '(' + allDescriptorsCount + ' in total)'); + } + reportNamespacesCacheMutation(); + return namespacesCache; + }); + + extractNamespacesAsyncInFlightPromise.then(result => { + extractNamespacesAsyncInFlightPromise = null; + }); + return extractNamespacesAsyncInFlightPromise; +} + +export function invalidateNamespacesCache() { + if (toggles.DEBUG_CACHES) { + console.log('invalidateNamespacesCache'); + } + namespacesCache = null; +} + +function extractSourceCodeNamespacesAsync(uiSourceCode) { + if (!isRelevantSourceCode(uiSourceCode)) { + return Promise.resolve({}); + } + + return getSourceCodeNamespaceDescriptorsAsync(uiSourceCode).then(prepareNamespacesFromDescriptors); +} + +function extractAndMergeSourceCodeNamespacesAsync(uiSourceCode) { + if (!isRelevantSourceCode(uiSourceCode)) { + console.warn('extractAndMergeSourceCodeNamespacesAsync called on irrelevant source code', uiSourceCode); + return; + } + + if (toggles.DEBUG_CACHES) { + console.log('extractAndMergeSourceCodeNamespacesAsync', uiSourceCode); + } + const jobs = [extractNamespacesAsync(), extractSourceCodeNamespacesAsync(uiSourceCode)]; + return Promise.all(jobs).then(([namespaces, result]) => { + const addedNamespaceNames = Object.keys(result); + if (addedNamespaceNames.length) { + Object.assign(namespaces, result); + if (toggles.DEBUG_CACHES) { + console.log('updated namespacesCache by merging ', addedNamespaceNames, + 'from', uiSourceCode.contentURL(), + ' => new namespaces count:', Object.keys(namespaces).length); + } + reportNamespacesCacheMutation(); + } + return result; + }); +} + +function removeNamespacesMatchingUrl(url) { + extractNamespacesAsync().then(namespaces => { + const removedNames = []; + for (const namespaceName of Object.keys(namespaces)) { + const descriptor = namespaces[namespaceName]; + if (descriptor.url === url) { + delete namespaces[namespaceName]; + removedNames.push(namespaceName); + } + } + + if (toggles.DEBUG_CACHES) { + console.log('removeNamespacesMatchingUrl removed ' + removedNames.length + ' namespaces for url: ' + url + + ' new namespaces count:' + Object.keys(namespaces).length); + } + }); +} + +// --- namespace symbols ------------------------------------------------------------------------------------------------ + +/** + * @param {!Array} uiSourceCodes + * @param {function(string)} urlMatcherFn + * @return {!Array} + */ +function findMatchingSourceCodes(uiSourceCodes, urlMatcherFn) { + const matching = []; + for (let i = 0; i < uiSourceCodes.length; i++) { + const uiSourceCode = uiSourceCodes[i]; + if (urlMatcherFn(uiSourceCode.url())) { + matching.push(uiSourceCode); + } + } + return matching; +} + +/** + * @param {!Array} names + * @param {string} namespaceName + * @return {!Array} + */ +function filterNamesForNamespace(names, namespaceName) { + const prefix = namespaceName + '/'; + const prefixLength = prefix.length; + + return names.filter(name => name.startsWith(prefix)).map(name => name.substring(prefixLength)); +} + +/** + * @param {!Workspace.UISourceCode} uiSourceCode + * @return {?SDK.Script} + */ +function getScriptFromSourceCode(uiSourceCode) { + const target = SDK.targetManager.mainTarget(); + if (!target) { + throw new Error( + 'getScriptFromSourceCode called when there is no main target\n' + + `uiSourceCode: name=${uiSourceCode.name()} url=${uiSourceCode.url()} project=${uiSourceCode.project().type()}\n`); + } + const debuggerModel = /** @type {!SDK.DebuggerModel} */ (target.model(SDK.DebuggerModel)); + if (!debuggerModel) { + throw new Error( + `getScriptFromSourceCode called when main target has no debuggerModel target=${target}\n` + + `uiSourceCode: name=${uiSourceCode.name()} url=${uiSourceCode.url()} project=${uiSourceCode.project().type()}\n`); + } + const scriptFile = Bindings.debuggerWorkspaceBinding.scriptFile(uiSourceCode, debuggerModel); + if (!scriptFile) { + // do not treat missing script file as a fatal error, only log error into internal dirac console + // see https://github.com/binaryage/dirac/issues/79 + + // disabled to prevent console spam + if (toggles.DEBUG_CACHES) { + console.error( + 'uiSourceCode expected to have scriptFile associated\n' + + `uiSourceCode: name=${uiSourceCode.name()} url=${uiSourceCode.url()} project=${uiSourceCode.project().type()}\n`); + } + return null; + } + const script = scriptFile._script; + if (!script) { + throw new Error( + 'uiSourceCode expected to have _script associated\n' + + `uiSourceCode: name=${uiSourceCode.name()} url=${uiSourceCode.url()} project=${uiSourceCode.project().type()}\n`); + } + if (!(script instanceof SDK.Script)) { + throw new Error( + 'getScriptFromSourceCode expected to return an instance of SDK.Script\n' + + `uiSourceCode: name=${uiSourceCode.name()} url=${uiSourceCode.url()} project=${uiSourceCode.project().type()}\n`); + } + return script; +} + +function extractNamesFromSourceMap(uiSourceCode, namespaceName) { + const script = getScriptFromSourceCode(uiSourceCode); + if (!script) { + console.error('unable to locate script when extracting symbols for ClojureScript namespace \'' + namespaceName + '\''); + return []; + } + const sourceMap = Bindings.debuggerWorkspaceBinding.sourceMapForScript(/** @type {!SDK.Script} */(script)); + if (!sourceMap) { + console.error('unable to locate sourceMap when extracting symbols for ClojureScript namespace \'' + namespaceName + '\''); + return []; + } + const payload = sourceMap._payload; + if (!payload) { + console.error('unable to locate payload when extracting symbols for ClojureScript namespace \'' + namespaceName + '\''); + return []; + } + return payload.names || []; +} + +function extractNamespaceSymbolsAsyncWorker(namespaceName) { + const workspace = Workspace.workspace; + if (!workspace) { + console.error('unable to locate Workspace when extracting symbols for ClojureScript namespace \'' + namespaceName + '\''); + return Promise.resolve([]); + } + + return new Promise(resolve => { + const urlMatcherFn = prepareUrlMatcher(namespaceName); + const uiSourceCodes = getRelevantSourceCodes(workspace); + + // not there may be multiple matching sources for given namespaceName + // figwheel reloading is just adding new files and not removing old ones + const matchingSourceCodes = findMatchingSourceCodes(uiSourceCodes, urlMatcherFn); + if (!matchingSourceCodes.length) { + if (toggles.DEBUG_CACHES) { + console.warn('cannot find any matching source file for ClojureScript namespace \'' + namespaceName + '\''); + } + resolve([]); + return; + } + + // we simply extract names from all matching source maps and then we filter them to match our namespace name and + // deduplicate them + const results = []; + for (const uiSourceCode of matchingSourceCodes) { + results.push(extractNamesFromSourceMap(uiSourceCode, namespaceName)); + } + const allNames = [].concat.apply([], results); + const filteredNames = unique(filterNamesForNamespace(allNames, namespaceName)); + + if (toggles.DEBUG_CACHES) { + console.log('extracted ' + filteredNames.length + ' symbol names for namespace', namespaceName, matchingSourceCodes.map(i => i.url())); + } + + resolve(filteredNames); + }); +} + +export function extractNamespaceSymbolsAsync(namespaceName) { + if (!namespaceName) { + return Promise.resolve([]); + } + + if (namespacesSymbolsCache.has(namespaceName)) { + return namespacesSymbolsCache.get(namespaceName); + } + + const promisedResult = extractNamespaceSymbolsAsyncWorker(namespaceName); + + namespacesSymbolsCache.set(namespaceName, promisedResult); + + startListeningForWorkspaceChanges(); + return promisedResult; +} + +export function invalidateNamespaceSymbolsCache(namespaceName = null) { + if (toggles.DEBUG_CACHES) { + console.log('invalidateNamespaceSymbolsCache', namespaceName); + } + if (namespaceName) { + namespacesSymbolsCache.delete(namespaceName); + } else { + namespacesSymbolsCache.clear(); + } +} + +// --- macro namespaces symbols ----------------------------------------------------------------------------------------- +// +// a situation is a bit more tricky here +// we don't have source mapping to clojure land in case of macro .clj files (makes no sense) +// but thanks to our access to all existing (ns ...) forms in the project we can infer at least some information +// we can at least collect macro symbols referred to via :refer + +function extractMacroNamespaceSymbolsAsyncWorker(namespaceName) { + + const collectMacroSymbols = namespaceDescriptors => { + const symbols = []; + for (const descriptor of Object.values(namespaceDescriptors)) { + const refers = descriptor.macroRefers; + if (!refers) { + continue; + } + for (const symbol of Object.keys(refers)) { + const ns = refers[symbol]; + if (ns === namespaceName) { + symbols.push(symbol); + } + } + } + return deduplicate(symbols); + }; + + return extractNamespacesAsync().then(collectMacroSymbols); +} + +export function extractMacroNamespaceSymbolsAsync(namespaceName) { + if (!namespaceName) { + return Promise.resolve([]); + } + + const promisedResult = extractMacroNamespaceSymbolsAsyncWorker(namespaceName); + + if (toggles.DEBUG_CACHES) { + promisedResult.then(result => { + console.log('extractMacroNamespaceSymbolsAsync resolved', namespaceName, result); + }); + } + + return promisedResult; +} + +// --- changes ---------------------------------------------------------------------------------------------------------- +// this is to reflect dynamically updated files e.g. by Figwheel + +let listeningForWorkspaceChanges = false; + +function invalidateNamespaceSymbolsMatchingUrl(url) { + for (const namespaceName of namespacesSymbolsCache.keys()) { + const matcherFn = prepareUrlMatcher(namespaceName); + if (matcherFn(url)) { + invalidateNamespaceSymbolsCache(namespaceName); + } + } +} + +function handleSourceCodeAdded(event) { + const uiSourceCode = event.data; + if (uiSourceCode && isRelevantSourceCode(uiSourceCode)) { + const url = uiSourceCode.url(); + if (toggles.DEBUG_WATCHING) { + console.log('handleSourceCodeAdded', url); + } + extractAndMergeSourceCodeNamespacesAsync(uiSourceCode); + invalidateNamespaceSymbolsMatchingUrl(url); + } +} + +function handleSourceCodeRemoved(event) { + const uiSourceCode = event.data; + if (uiSourceCode && isRelevantSourceCode(uiSourceCode)) { + const url = uiSourceCode.url(); + if (toggles.DEBUG_WATCHING) { + console.log('handleSourceCodeRemoved', url); + } + removeNamespacesMatchingUrl(url); + invalidateNamespaceSymbolsMatchingUrl(url); + } +} + +export function startListeningForWorkspaceChanges() { + if (listeningForWorkspaceChanges) { + return; + } + + if (toggles.DEBUG_WATCHING) { + console.log('startListeningForWorkspaceChanges'); + } + + const workspace = Workspace.workspace; + if (!workspace) { + console.error('unable to locate Workspace in startListeningForWorkspaceChanges'); + return; + } + + workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, handleSourceCodeAdded, dirac); + workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeRemoved, handleSourceCodeRemoved, dirac); + + listeningForWorkspaceChanges = true; +} + +export function stopListeningForWorkspaceChanges() { + if (!listeningForWorkspaceChanges) { + return; + } + + if (toggles.DEBUG_WATCHING) { + console.log('stopListeningForWorkspaceChanges'); + } + + const workspace = Workspace.workspace; + if (!workspace) { + console.error('unable to locate Workspace in stopListeningForWorkspaceChanges'); + return; + } + + workspace.removeEventListener(Workspace.Workspace.Events.UISourceCodeAdded, handleSourceCodeAdded, dirac); + workspace.removeEventListener(Workspace.Workspace.Events.UISourceCodeRemoved, handleSourceCodeRemoved, dirac); + + listeningForWorkspaceChanges = false; +} + +export let diracLinkHandlerAction = null; + +export function registerDiracLinkAction(action) { + if (diracLinkHandlerAction) { + throw new Error('registerDiracLinkAction already set'); + } + diracLinkHandlerAction = action; +} + +export function getPanel(name) { + return globalThis.UI.panels[name]; +} + +// we have to use this extension mechanism because dirac ES6 module object is not extensible +export const extension = {}; + +// note: there will be more functions added to this object dynamically by implant init code + +/** + * @returns {undefined} + * @param {any[]} args + */ +export function feedback(...args) { + return extension.feedback(...arguments); +} + +/** + * @returns {undefined} + */ +export function initConsole() { + return extension.initConsole(...arguments); +} + +/** + * @returns {undefined} + */ +export function initRepl() { + return extension.initRepl(...arguments); +} + +/** + * @param {string} panelId + */ +export function notifyPanelSwitch(panelId) { + return extension.notifyPanelSwitch(...arguments); +} + +/** + */ +export function notifyFrontendInitialized() { + return extension.notifyFrontendInitialized(...arguments); +} + +/** + * @param {HTMLElement} textArea + * @param {boolean} useParinfer + * @returns {!CodeMirror} + */ +export function adoptPrompt(textArea, useParinfer) { + return extension.adoptPrompt(...arguments); +} + +/** + * @param {number} requestId + * @param {string} code + * @param {?object} scopeInfo + */ +export function sendEvalRequest(requestId, code, scopeInfo) { + return extension.sendEvalRequest(...arguments); +} + +/** + * @returns {string} + */ +export function getVersion() { + return extension.getVersion(...arguments); +} + +/** + * @param {Function} callback + * @returns {promise} + */ +export function getRuntimeTag(callback) { + return extension.getRuntimeTag(...arguments); +} + +/** + * @param {string} source + * @returns {object} + */ +export function parseNsFromSource(source) { + return extension.parseNsFromSource(...arguments); +} + +/** + * @param {string} ns + * @param {string} ext + * @returns {string} + */ +export function nsToRelpath(ns, ext) { + return extension.nsToRelpath(...arguments); +} + +/** + */ +export function triggerInternalError() { + return extension.triggerInternalError(...arguments); +} + +export function triggerInternalErrorInPromise() { + return extension.triggerInternalErrorInPromise(...arguments); +} + +export function triggerInternalErrorAsErrorLog() { + return extension.triggerInternalErrorAsErrorLog(...arguments); +} + +/** + * @param {string} mungedName + * @returns {string} + */ +export function getFunctionName(mungedName) { + return extension.getFunctionName(...arguments); +} + +/** + * @param {string} mungedName + * @returns {string} + */ +export function getFullFunctionName(mungedName) { + return extension.getFullFunctionName(...arguments); +} + +/** + * @returns {promise} + */ +export function getReplSpecialsAsync() { + return extension.getReplSpecialsAsync(...arguments); +} + +/** + * @returns {boolean} + */ +export function isIntercomReady() { + return extension.isIntercomReady(...arguments); +} + +export function reportNamespacesCacheMutation() { + return extension.reportNamespacesCacheMutation(...arguments); +} + +// eslint-disable-next-line no-console +console.log('dirac module imported!'); + +export {Keysim}; diff --git a/front_end/dirac/dirac.js b/front_end/dirac/dirac.js index 88b7f6079bc..27fb5bbd3f7 100644 --- a/front_end/dirac/dirac.js +++ b/front_end/dirac/dirac.js @@ -1,333 +1,18 @@ -// @ts-nocheck -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. +/* eslint-disable rulesdir/check_license_header,no-console */ -import './keysim.js'; -import './parinfer.js'; -import './parinfer-codemirror.js'; +import * as DiracAngel from './DiracAngel.js'; -console.log('dirac module import!'); +console.log('dirac API import!'); -(function () { - const window = this; +export { DiracAngel }; +export * from './DiracAngel.js'; - // dirac namespace may not exist at this point, play safe - if (!window.dirac) { - window.dirac = {}; - } +// this is dirty, make dirac globally available for implant code +globalThis.diracAngel = DiracAngel; - // note: if goog/cljs namespace system comes after us, they don't wipe our properties, they just merge theirs in - Object.assign(window.dirac, (function () { - const readyPromise = new Promise(fulfil => window.dirac._runtimeReadyPromiseCallback = fulfil); +globalThis.initDiracImplantAfterLoad = true; +// eslint-disable-next-line no-console +console.log('implant will be initialized after load'); - function getReadyPromise() { - return readyPromise; - } - - function markAsReady() { - window.dirac._runtimeReadyPromiseCallback(); - } - - const featureFlags = {}; - - // WARNING: keep this in sync with dirac.background.tools/flag-keys - const knownFeatureFlags = [ - 'enable-repl', - 'enable-parinfer', - 'enable-friendly-locals', - 'enable-clustered-locals', - 'inline-custom-formatters', - 'welcome-message', - 'clean-urls', - 'beautify-function-names', - 'link-actions']; - - function hasFeature(feature) { - const flag = featureFlags[feature]; - if (flag !== undefined) { - return flag; - } - const featureIndex = knownFeatureFlags.indexOf(feature); - if (featureIndex === -1) { - return true; - } - const activeFlags = Root.Runtime.queryParam('dirac_flags') || ''; - const result = activeFlags[featureIndex] !== '0'; - featureFlags[feature] = result; - return result; - } - - function getToggle(name) { - if (window.dirac.DEBUG_TOGGLES) { - console.log("dirac: get toggle '" + name + "' => " + window.dirac[name]); - } - return window.dirac[name]; - } - - function setToggle(name, value) { - if (window.dirac.DEBUG_TOGGLES) { - console.log("dirac: set toggle '" + name + "' => " + value); - } - window.dirac[name] = value; - } - - function hasDebugFlag(flagName) { - if (Root.Runtime.queryParam('debug_all') === '1') { - return true; - } - const paramName = 'debug_' + flagName.toLowerCase(); - return Root.Runtime.queryParam(paramName) === '1'; - } - - // taken from https://github.com/joliss/js-string-escape/blob/master/index.js - function stringEscape(string) { - return ('' + string).replace(/["'\\\n\r\u2028\u2029]/g, function (character) { - // Escape all characters not included in SingleStringCharacters and - // DoubleStringCharacters on - // http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4 - switch (character) { - case '"': - case "'": - case '\\': - return '\\' + character; - // Four possible LineTerminator characters need to be escaped: - case '\n': - return '\\n'; - case '\r': - return '\\r'; - case '\u2028': - return '\\u2028'; - case '\u2029': - return '\\u2029'; - } - }); - } - - function codeAsString(code) { - return "'" + stringEscape(code) + "'"; - } - - function loadLazyDirac() { - return window.runtime.loadModulePromise('dirac_lazy'); - } - - function deduplicate(coll, keyFn = item => '' + item) { - const store = new Set(); - return coll.filter(item => !store.has(keyFn(item)) && !!store.add(keyFn(item))); - } - - // http://stackoverflow.com/a/20767836/84283 - function stableSort(array, comparator) { - const wrapped = array.map((d, i) => ({d: d, i: i})); - - wrapped.sort((a, b) => { - const cmp = comparator(a.d, b.d); - return cmp === 0 ? a.i - b.i : cmp; - }); - - return wrapped.map(wrapper => wrapper.d); - } - - function getNamespace(namespaceName) { - if (!dirac.namespacesCache) { - return; - } - - return dirac.namespacesCache[namespaceName]; - } - - function dispatchEventsForAction(action) { - return new Promise(resolve => { - const continuation = () => resolve("performed document action: '" + action + "'"); - const keyboard = Keysim.Keyboard.US_ENGLISH; - keyboard.dispatchEventsForAction(action, window['document'], continuation); - }); - } - - /** - * @suppressGlobalPropertiesCheck - **/ - function collectShadowRoots(root = null) { - const res = []; - const startNode = root || document.body; - for (let node = startNode; node; node = node.traverseNextNode(startNode)) { - if (node instanceof ShadowRoot) { - res.push(node); - } - } - return res; - } - - function querySelectorAllDeep(node, query) { - const roots = [node].concat(collectShadowRoots(node)); - let res = []; - for (const node of roots) { - const partial = node.querySelectorAll(query); - res = res.concat(Array.from(partial)); - } - return res; - } - - // --- lazy APIs -------------------------------------------------------------------------------------------------------- - // calling any of these functions will trigger loading dirac_lazy overlay - // which will eventually overwrite those functions when fully loaded - - function startListeningForWorkspaceChanges(...args) { - return loadLazyDirac().then(() => window.dirac.startListeningForWorkspaceChanges(...args)); - } - - function stopListeningForWorkspaceChanges(...args) { - return loadLazyDirac().then(() => window.dirac.stopListeningForWorkspaceChanges(...args)); - } - - function extractScopeInfoFromScopeChainAsync(...args) { - return loadLazyDirac().then(() => window.dirac.extractScopeInfoFromScopeChainAsync(...args)); - } - - function extractNamespaceSymbolsAsync(...args) { - return loadLazyDirac().then(() => window.dirac.extractNamespaceSymbolsAsync(...args)); - } - - function invalidateNamespaceSymbolsCache(...args) { - return loadLazyDirac().then(() => window.dirac.invalidateNamespaceSymbolsCache(...args)); - } - - function extractMacroNamespaceSymbolsAsync(...args) { - return loadLazyDirac().then(() => window.dirac.extractMacroNamespaceSymbolsAsync(...args)); - } - - function extractNamespacesAsync() { - return loadLazyDirac().then(() => window.dirac.extractNamespacesAsync()); - } - - function invalidateNamespacesCache(...args) { - return loadLazyDirac().then(() => window.dirac.invalidateNamespacesCache(...args)); - } - - function getMacroNamespaceNames(...args) { - return loadLazyDirac().then(() => window.dirac.getMacroNamespaceNames(...args)); - } - - function lookupCurrentContext(...args) { - return loadLazyDirac().then(() => window.dirac.lookupCurrentContext(...args)); - } - - function evalInCurrentContext(...args) { - return loadLazyDirac().then(() => window.dirac.evalInCurrentContext(...args)); - } - - function hasCurrentContext() { - return loadLazyDirac().then(() => window.dirac.hasCurrentContext()); - } - - function evalInDefaultContext(...args) { - return loadLazyDirac().then(() => window.dirac.evalInDefaultContext(...args)); - } - - function hasDefaultContext() { - return loadLazyDirac().then(() => window.dirac.hasDefaultContext()); - } - - function getMainDebuggerModel(...args) { - return loadLazyDirac().then(() => window.dirac.getMainDebuggerModel(...args)); - } - - function subscribeDebuggerEvents(...args) { - return loadLazyDirac().then(() => window.dirac.subscribeDebuggerEvents(...args)); - } - - function unsubscribeDebuggerEvents(...args) { - return loadLazyDirac().then(() => window.dirac.unsubscribeDebuggerEvents(...args)); - } - - function addConsoleMessageToMainTarget(...args) { - return loadLazyDirac().then(() => window.dirac.addConsoleMessageToMainTarget(...args)); - } - - function evaluateCommandInConsole(...args) { - return loadLazyDirac().then(() => window.dirac.evaluateCommandInConsole(...args)); - } - - function registerDiracLinkAction(...args) { - return loadLazyDirac().then(() => window.dirac.registerDiracLinkAction(...args)); - } - -// --- exported interface --------------------------------------------------------------------------------------------------- - - // don't forget to update externs.js too - return { - DEBUG_EVAL: hasDebugFlag('eval'), - DEBUG_COMPLETIONS: hasDebugFlag('completions'), - DEBUG_KEYSIM: hasDebugFlag('keysim'), - DEBUG_FEEDBACK: hasDebugFlag('feedback'), - DEBUG_WATCHING: hasDebugFlag('watching'), - DEBUG_CACHES: hasDebugFlag('caches'), - DEBUG_TOGGLES: hasDebugFlag('toggles'), - - // we use can_dock url param indicator if we are launched as internal devtools - hostedInExtension: !Root.Runtime.queryParam('can_dock'), - - // -- feature toggles ----------------------------------------------------------------------------------------------- - hasREPL: hasFeature('enable-repl'), - hasParinfer: hasFeature('enable-parinfer'), - hasFriendlyLocals: hasFeature('enable-friendly-locals'), - hasClusteredLocals: hasFeature('enable-clustered-locals'), - hasInlineCFs: hasFeature('inline-custom-formatters'), - hasWelcomeMessage: hasFeature('welcome-message'), - hasCleanUrls: hasFeature('clean-urls'), - hasBeautifyFunctionNames: hasFeature('beautify-function-names'), - hasLinkActions: hasFeature('link-actions'), - - // -- INTERFACE ----------------------------------------------------------------------------------------------------- - getReadyPromise: getReadyPromise, - markAsReady: markAsReady, - hasFeature: hasFeature, - codeAsString: codeAsString, - stringEscape: stringEscape, - deduplicate: deduplicate, - stableSort: stableSort, - getNamespace: getNamespace, - dispatchEventsForAction: dispatchEventsForAction, - querySelectorAllDeep: querySelectorAllDeep, - setToggle: setToggle, - getToggle: getToggle, - - // -- LAZY INTERFACE ------------------------------------------------------------------------------------------------ - lookupCurrentContext: lookupCurrentContext, - evalInCurrentContext: evalInCurrentContext, - hasCurrentContext: hasCurrentContext, - evalInDefaultContext: evalInDefaultContext, - hasDefaultContext: hasDefaultContext, - getMainDebuggerModel: getMainDebuggerModel, - subscribeDebuggerEvents: subscribeDebuggerEvents, - unsubscribeDebuggerEvents: unsubscribeDebuggerEvents, - addConsoleMessageToMainTarget: addConsoleMessageToMainTarget, - evaluateCommandInConsole: evaluateCommandInConsole, - startListeningForWorkspaceChanges: startListeningForWorkspaceChanges, - stopListeningForWorkspaceChanges: stopListeningForWorkspaceChanges, - extractScopeInfoFromScopeChainAsync: extractScopeInfoFromScopeChainAsync, - extractNamespaceSymbolsAsync: extractNamespaceSymbolsAsync, - extractMacroNamespaceSymbolsAsync: extractMacroNamespaceSymbolsAsync, - extractNamespacesAsync: extractNamespacesAsync, - invalidateNamespaceSymbolsCache: invalidateNamespaceSymbolsCache, - invalidateNamespacesCache: invalidateNamespacesCache, - getMacroNamespaceNames: getMacroNamespaceNames, - registerDiracLinkAction: registerDiracLinkAction, - - // ... - - // note: there will be more functions added to this object dynamically by dirac.implant init code - // see externs.js for full list of avail functions - }; - - })()); - - if (window.dirac.implant) { - window.dirac.implant.init_implant(); - } else { - window.initDiracImplantAfterLoad = true; - } -}).call(self); - -console.log('dirac module imported!'); +// eslint-disable-next-line no-console +console.log('dirac API import done'); diff --git a/front_end/dirac/keysim.js b/front_end/dirac/keysim.js index 75450af52bc..23cab13af3d 100644 --- a/front_end/dirac/keysim.js +++ b/front_end/dirac/keysim.js @@ -1,803 +1,779 @@ -// @ts-nocheck // Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - factory((global.Keysim = {})); -})(self, function (exports) { - 'use strict'; - - const _createClass = (function () { - function defineProperties(target, props) { - for (let i = 0; i < props.length; i++) { - const descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ('value' in descriptor) { - descriptor.writable = true; - } - Object.defineProperty(target, descriptor.key, descriptor); - } - } - - return function (Constructor, protoProps, staticProps) { - if (protoProps) { - defineProperties(Constructor.prototype, protoProps); - } - if (staticProps) { - defineProperties(Constructor, staticProps); +// @ts-nocheck +const _createClass = (function() { + function defineProperties(target, props) { + for (let i = 0; i < props.length; i++) { + const descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ('value' in descriptor) { + descriptor.writable = true; } - return Constructor; - }; - })(); - - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError('Cannot call a class as a function'); + Object.defineProperty(target, descriptor.key, descriptor); } } - /** - * @param {string} s - * @return {function(string):string} - */ - const appender = function (s) { - return function (value) { - return value + s; - }; + return function(Constructor, protoProps, staticProps) { + if (protoProps) { + defineProperties(Constructor.prototype, protoProps); + } + if (staticProps) { + defineProperties(Constructor, staticProps); + } + return Constructor; }; +})(); - /** - * @param {number} n - * @return {function(string):string} - */ - const deleter = function (n) { - return function (value) { - const end = value.length - n; - return (end > 0) ? value.substring(0, end) : ''; - }; +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError('Cannot call a class as a function'); + } +} + +/** + * @param {string} s + * @return {function(string):string} + */ +const appender = function(s) { + return function(value) { + return value + s; + }; +}; + +/** + * @param {number} n + * @return {function(string):string} + */ +const deleter = function(n) { + return function(value) { + const end = value.length - n; + return (end > 0) ? value.substring(0, end) : ''; }; +}; - let taskQueueRunning = false; - const taskQueue = []; +let taskQueueRunning = false; +const taskQueue = []; - function processTask(task) { - try { - task.job(); - } catch (e) { - console.error('keysim task has failed:', e, '\n', task); - } - wakeTaskQueue(); +function processTask(task) { + try { + task.job(); + } catch (e) { + console.error('keysim task has failed:', e, '\n', task); } - - function wakeTaskQueue() { - const task = taskQueue.shift(); - if (task) { - taskQueueRunning = true; - setTimeout(function () { - processTask(task); - }, task.delay); - } else { - taskQueueRunning = false; - } + wakeTaskQueue(); +} + +function wakeTaskQueue() { + const task = taskQueue.shift(); + if (task) { + taskQueueRunning = true; + setTimeout(function() { + processTask(task); + }, task.delay); + } else { + taskQueueRunning = false; } +} - function scheduleTask(delay, job) { - taskQueue.push({delay, job, stack: new Error('scheduled at')}); - if (!taskQueueRunning) { - wakeTaskQueue(); - } +function scheduleTask(delay, job) { + taskQueue.push({delay, job, stack: new Error('scheduled at')}); + if (!taskQueueRunning) { + wakeTaskQueue(); } - - /* jshint esnext:true, undef:true, unused:true */ - - // taken from devtools.js - // noinspection DuplicatedCode - const staticKeyIdentifiers = new Map([ - [0x12, 'Alt'], - [0x11, 'Control'], - [0x10, 'Shift'], - [0x14, 'CapsLock'], - [0x5b, 'Win'], - [0x5c, 'Win'], - [0x0c, 'Clear'], - [0x28, 'Down'], - [0x23, 'End'], - [0x0a, 'Enter'], - [0x0d, 'Enter'], - [0x2b, 'Execute'], - [0x70, 'F1'], - [0x71, 'F2'], - [0x72, 'F3'], - [0x73, 'F4'], - [0x74, 'F5'], - [0x75, 'F6'], - [0x76, 'F7'], - [0x77, 'F8'], - [0x78, 'F9'], - [0x79, 'F10'], - [0x7a, 'F11'], - [0x7b, 'F12'], - [0x7c, 'F13'], - [0x7d, 'F14'], - [0x7e, 'F15'], - [0x7f, 'F16'], - [0x80, 'F17'], - [0x81, 'F18'], - [0x82, 'F19'], - [0x83, 'F20'], - [0x84, 'F21'], - [0x85, 'F22'], - [0x86, 'F23'], - [0x87, 'F24'], - [0x2f, 'Help'], - [0x24, 'Home'], - [0x2d, 'Insert'], - [0x25, 'Left'], - [0x22, 'PageDown'], - [0x21, 'PageUp'], - [0x13, 'Pause'], - [0x2c, 'PrintScreen'], - [0x27, 'Right'], - [0x91, 'Scroll'], - [0x29, 'Select'], - [0x26, 'Up'], - [0x2e, 'U+007F'], // Standard says that DEL becomes U+007F. - [0xb0, 'MediaNextTrack'], - [0xb1, 'MediaPreviousTrack'], - [0xb2, 'MediaStop'], - [0xb3, 'MediaPlayPause'], - [0xad, 'VolumeMute'], - [0xae, 'VolumeDown'], - [0xaf, 'VolumeUp'], - ]); - - function keyCodeToKeyIdentifier(keyCode) { - let result = staticKeyIdentifiers.get(keyCode); - if (result !== undefined) { - return result; - } - result = 'U+'; - const hexString = Number(keyCode).toString(16).toUpperCase(); - for (let i = hexString.length; i < 4; ++i) { - result += '0'; - } - result += hexString; +} + +/* jshint esnext:true, undef:true, unused:true */ + +// taken from devtools.js +// noinspection DuplicatedCode +const staticKeyIdentifiers = new Map([ + [0x12, 'Alt'], + [0x11, 'Control'], + [0x10, 'Shift'], + [0x14, 'CapsLock'], + [0x5b, 'Win'], + [0x5c, 'Win'], + [0x0c, 'Clear'], + [0x28, 'Down'], + [0x23, 'End'], + [0x0a, 'Enter'], + [0x0d, 'Enter'], + [0x2b, 'Execute'], + [0x70, 'F1'], + [0x71, 'F2'], + [0x72, 'F3'], + [0x73, 'F4'], + [0x74, 'F5'], + [0x75, 'F6'], + [0x76, 'F7'], + [0x77, 'F8'], + [0x78, 'F9'], + [0x79, 'F10'], + [0x7a, 'F11'], + [0x7b, 'F12'], + [0x7c, 'F13'], + [0x7d, 'F14'], + [0x7e, 'F15'], + [0x7f, 'F16'], + [0x80, 'F17'], + [0x81, 'F18'], + [0x82, 'F19'], + [0x83, 'F20'], + [0x84, 'F21'], + [0x85, 'F22'], + [0x86, 'F23'], + [0x87, 'F24'], + [0x2f, 'Help'], + [0x24, 'Home'], + [0x2d, 'Insert'], + [0x25, 'Left'], + [0x22, 'PageDown'], + [0x21, 'PageUp'], + [0x13, 'Pause'], + [0x2c, 'PrintScreen'], + [0x27, 'Right'], + [0x91, 'Scroll'], + [0x29, 'Select'], + [0x26, 'Up'], + [0x2e, 'U+007F'], // Standard says that DEL becomes U+007F. + [0xb0, 'MediaNextTrack'], + [0xb1, 'MediaPreviousTrack'], + [0xb2, 'MediaStop'], + [0xb3, 'MediaPlayPause'], + [0xad, 'VolumeMute'], + [0xae, 'VolumeDown'], + [0xaf, 'VolumeUp'], +]); + +function keyCodeToKeyIdentifier(keyCode) { + let result = staticKeyIdentifiers.get(keyCode); + if (result !== undefined) { return result; } - - const keyCodeToKeyMap = { - 9: 'Tab', // tab - 16: 'Shift', - 27: 'Escape', // esc - 32: ' ', // space - 38: 'ArrowUp', - 40: 'ArrowDown', - 37: 'ArrowLeft', - 39: 'ArrowRight', - 13: 'Enter', - 112: 'F1', - 113: 'F2', - 114: 'F3', - 115: 'F4', - 116: 'F5', - 117: 'F6', - 118: 'F7', - 119: 'F8', - 120: 'F9', - 121: 'F10', - 122: 'F11', - 123: 'F12', - 46: 'U+007F', - 36: 'Home', - 35: 'End', - 33: 'PageUp', - 34: 'PageDown', - 45: 'Insert' - }; - - function keyCodeToKey(keyCode) { - return keyCodeToKeyMap[keyCode] || String.fromCharCode(keyCode); + result = 'U+'; + const hexString = Number(keyCode).toString(16).toUpperCase(); + for (let i = hexString.length; i < 4; ++i) { + result += '0'; } - - - const CTRL = 1 << 0; - const META = 1 << 1; - const ALT = 1 << 2; - const SHIFT = 1 << 3; - - // Key Events - const KeyEvents = { - DOWN: 1 << 0, - PRESS: 1 << 1, - UP: 1 << 2, - INPUT: 1 << 3 - }; - KeyEvents.ALL = KeyEvents.DOWN | KeyEvents.PRESS | KeyEvents.UP | KeyEvents.INPUT; - - /** - * Represents a keystroke, or a single key code with a set of active modifiers. - * @constructor - * @param {number} modifiers A bitmask formed by CTRL, META, ALT, and SHIFT. - * @param {number} keyCode - * @param {?function(string):string} mutation - */ - const Keystroke = (function () { - /** @this {Keystroke} */ - function Keystroke(modifiers, keyCode, mutation = null) { - _classCallCheck(this, Keystroke); - - this.modifiers = modifiers; - this.ctrlKey = !!(modifiers & CTRL); - this.metaKey = !!(modifiers & META); - this.altKey = !!(modifiers & ALT); - this.shiftKey = !!(modifiers & SHIFT); - this.keyCode = keyCode; - this.mutation = mutation; - } - - /** - * Simulates a keyboard with a particular key-to-character and key-to-action - * mapping. Use `US_ENGLISH` to get a pre-configured keyboard. - */ - - /** - * Gets the bitmask value for the "control" modifier. - */ - _createClass(Keystroke, null, [{ - key: 'CTRL', - value: CTRL, - enumerable: true - }, { - key: 'META', - value: META, - enumerable: true - }, { - key: 'ALT', - value: ALT, - enumerable: true - }, { - key: 'SHIFT', - value: SHIFT, - enumerable: true - }]); - - return Keystroke; - })(); - - const Keyboard = (function () { - /** - * @constructor - * @param {!Object.} charCodeKeyCodeMap - * @param {!Object.} actionMap - */ - function Keyboard(charCodeKeyCodeMap, actionMap) { - _classCallCheck(this, Keyboard); - - this._charCodeKeyCodeMap = charCodeKeyCodeMap; - this._actionMap = actionMap; - } - - /** - * Determines the character code generated by pressing the given keystroke. - * - * @param {!Keystroke} keystroke - * @return {?number} - * @this {Keyboard} - */ - Keyboard.prototype.charCodeForKeystroke = function charCodeForKeystroke(keystroke) { - const map = this._charCodeKeyCodeMap; - for (const charCode in map) { - if (Object.prototype.hasOwnProperty.call(map, charCode)) { - const keystrokeForCharCode = map[charCode]; - if (keystroke.keyCode === keystrokeForCharCode.keyCode && keystroke.modifiers === keystrokeForCharCode.modifiers) { - return parseInt(charCode, 10); - } - } - } - return null; - }; - - /** - * Creates an event ready for dispatching onto the given target. - * - * @param {string} type One of "keydown", "keypress", "keyup", or "input". - * @param {!Keystroke} keystroke - * @param {!HTMLElement} target - * @return {!Event} - * @this {Keyboard} - */ - Keyboard.prototype.createEventFromKeystroke = function createEventFromKeystroke(type, keystroke, target) { - let doc = target.ownerDocument || document; - if (target instanceof Document) { - doc = target; + result += hexString; + return result; +} + +const keyCodeToKeyMap = { + 9: 'Tab', // tab + 16: 'Shift', + 27: 'Escape', // esc + 32: ' ', // space + 38: 'ArrowUp', + 40: 'ArrowDown', + 37: 'ArrowLeft', + 39: 'ArrowRight', + 13: 'Enter', + 112: 'F1', + 113: 'F2', + 114: 'F3', + 115: 'F4', + 116: 'F5', + 117: 'F6', + 118: 'F7', + 119: 'F8', + 120: 'F9', + 121: 'F10', + 122: 'F11', + 123: 'F12', + 46: 'U+007F', + 36: 'Home', + 35: 'End', + 33: 'PageUp', + 34: 'PageDown', + 45: 'Insert' +}; + +function keyCodeToKey(keyCode) { + return keyCodeToKeyMap[keyCode] || String.fromCharCode(keyCode); +} + + +const CTRL = 1 << 0; +const META = 1 << 1; +const ALT = 1 << 2; +const SHIFT = 1 << 3; + +// Key Events +const KeyEvents = { + DOWN: 1 << 0, + PRESS: 1 << 1, + UP: 1 << 2, + INPUT: 1 << 3 +}; +KeyEvents.ALL = KeyEvents.DOWN | KeyEvents.PRESS | KeyEvents.UP | KeyEvents.INPUT; + +/** + * Represents a keystroke, or a single key code with a set of active modifiers. + * @constructor + * @param {number} modifiers A bitmask formed by CTRL, META, ALT, and SHIFT. + * @param {number} keyCode + * @param {?function(string):string} mutation + */ +function Keystroke(modifiers, keyCode, mutation = null) { + _classCallCheck(this, Keystroke); + + this.modifiers = modifiers; + this.ctrlKey = !!(modifiers & CTRL); + this.metaKey = !!(modifiers & META); + this.altKey = !!(modifiers & ALT); + this.shiftKey = !!(modifiers & SHIFT); + this.keyCode = keyCode; + this.mutation = mutation; +} + +/** + * Simulates a keyboard with a particular key-to-character and key-to-action + * mapping. Use `US_ENGLISH` to get a pre-configured keyboard. + */ + +/** + * Gets the bitmask value for the "control" modifier. + */ +_createClass(Keystroke, null, [{ + key: 'CTRL', + value: CTRL, + enumerable: true +}, { + key: 'META', + value: META, + enumerable: true +}, { + key: 'ALT', + value: ALT, + enumerable: true +}, { + key: 'SHIFT', + value: SHIFT, + enumerable: true +}]); + +function Keyboard(charCodeKeyCodeMap, actionMap) { + _classCallCheck(this, Keyboard); + + this._charCodeKeyCodeMap = charCodeKeyCodeMap; + this._actionMap = actionMap; +} + +/** + * Determines the character code generated by pressing the given keystroke. + * + * @param {!Keystroke} keystroke + * @return {?number} + * @this {Keyboard} + */ +Keyboard.prototype.charCodeForKeystroke = function charCodeForKeystroke(keystroke) { + const map = this._charCodeKeyCodeMap; + for (const charCode in map) { + if (Object.prototype.hasOwnProperty.call(map, charCode)) { + const keystrokeForCharCode = map[charCode]; + if (keystroke.keyCode === keystrokeForCharCode.keyCode && keystroke.modifiers === keystrokeForCharCode.modifiers) { + return parseInt(charCode, 10); } + } + } + return null; +}; + +/** + * Creates an event ready for dispatching onto the given target. + * + * @param {string} type One of "keydown", "keypress", "keyup", or "input". + * @param {!Keystroke} keystroke + * @param {!HTMLElement} target + * @return {!Event} + * @this {Keyboard} + */ +Keyboard.prototype.createEventFromKeystroke = function createEventFromKeystroke(type, keystroke, target) { + let doc = target.ownerDocument || document; + if (target instanceof Document) { + doc = target; + } - const window = doc.defaultView; - const Event = window.Event; + const window = doc.defaultView; + const Event = window.Event; - let event; + let event; - try { - event = new Event(type); - } catch (e) { - event = doc.createEvent('UIEvents'); - } + try { + event = new Event(type); + } catch (e) { + event = doc.createEvent('UIEvents'); + } - event.initEvent(type, true, true); - - switch (type) { - case 'input': - event.data = String.fromCharCode(this.charCodeForKeystroke(keystroke)); - break; - - case 'keydown': - case 'keypress': - case 'keyup': - event.shiftKey = keystroke.shiftKey; - event.altKey = keystroke.altKey; - event.metaKey = keystroke.metaKey; - event.ctrlKey = keystroke.ctrlKey; - event.keyCode = type === 'keypress' ? this.charCodeForKeystroke(keystroke) : keystroke.keyCode; - event.charCode = type === 'keypress' ? event.keyCode : 0; - event.which = event.keyCode; - event.keyIdentifier = keyCodeToKeyIdentifier(keystroke.keyCode); - event.key = keyCodeToKey(event.keyCode); - break; - } + event.initEvent(type, true, true); + + switch (type) { + case 'input': + event.data = String.fromCharCode(this.charCodeForKeystroke(keystroke)); + break; + + case 'keydown': + case 'keypress': + case 'keyup': + event.shiftKey = keystroke.shiftKey; + event.altKey = keystroke.altKey; + event.metaKey = keystroke.metaKey; + event.ctrlKey = keystroke.ctrlKey; + event.keyCode = type === 'keypress' ? this.charCodeForKeystroke(keystroke) : keystroke.keyCode; + event.charCode = type === 'keypress' ? event.keyCode : 0; + event.which = event.keyCode; + event.keyIdentifier = keyCodeToKeyIdentifier(keystroke.keyCode); + event.key = keyCodeToKey(event.keyCode); + break; + } - return event; - }; - - /** - * Fires the correct sequence of events on the given target as if the given - * action was undertaken by a human. - * - * @param {string} action e.g. "alt+shift+left" or "backspace" - * @param {!HTMLElement} target - * @param {?function()} callback - * @this {Keyboard} - */ - Keyboard.prototype.dispatchEventsForAction = function (action, target, callback) { - const keystroke = this.keystrokeForAction(action); - scheduleTask(50, () => this.dispatchEventsForKeystroke(keystroke, target)); - if (callback) { - scheduleTask(100, callback); - } - }; - - /** - * Fires the correct sequence of events on the given target as if the given - * input had been typed by a human. - * - * @param {string} input - * @param {!HTMLElement} target - * @param {?function(string):string} callback - * @this {Keyboard} - */ - Keyboard.prototype.dispatchEventsForInput = function (input, target, callback) { - let currentModifierState = 0; - for (let i = 0, _length = input.length; i < _length; i++) { - const keystroke = this.keystrokeForCharCode(input.charCodeAt(i)); - scheduleTask(30, ((currentModifierState, keystrokeModifiers) => - this.dispatchModifierStateTransition(target, currentModifierState, keystrokeModifiers)) - .bind(this, currentModifierState, keystroke.modifiers)); - scheduleTask(20, ((keystroke, char) => - this.dispatchEventsForKeystroke(keystroke, target, false, KeyEvents.ALL, appender(char))) - .bind(this, keystroke, input[i])); - currentModifierState = keystroke.modifiers; - } - scheduleTask(20, () => this.dispatchModifierStateTransition(target, currentModifierState, 0)); - if (callback) { - scheduleTask(100, callback); - } - }; - - /** - * Fires the correct sequence of events on the given target as if the given - * keystroke was performed by a human. When simulating, for example, typing - * the letter "A" (assuming a U.S. English keyboard) then the sequence will - * look like this: - * - * keydown keyCode=16 (SHIFT) charCode=0 shiftKey=true - * keydown keyCode=65 (A) charCode=0 shiftKey=true - * keypress keyCode=65 (A) charCode=65 (A) shiftKey=true - * input data=A - * keyup keyCode=65 (A) charCode=0 shiftKey=true - * keyup keyCode=16 (SHIFT) charCode=0 shiftKey=false - * - * If the keystroke would not cause a character to be input, such as when - * pressing alt+shift+left, the sequence looks like this: - * - * keydown keyCode=16 (SHIFT) charCode=0 altKey=false shiftKey=true - * keydown keyCode=18 (ALT) charCode=0 altKey=true shiftKey=true - * keydown keyCode=37 (LEFT) charCode=0 altKey=true shiftKey=true - * keyup keyCode=37 (LEFT) charCode=0 altKey=true shiftKey=true - * keyup keyCode=18 (ALT) charCode=0 altKey=false shiftKey=true - * keyup keyCode=16 (SHIFT) charCode=0 altKey=false shiftKey=false - * - * To disable handling of modifier keys, call with `transitionModifiers` set - * to false. Doing so will omit the keydown and keyup events associated with - * shift, ctrl, alt, and meta keys surrounding the actual keystroke. - * - * @param {!Keystroke} keystroke - * @param {!HTMLElement} target - * @param {boolean=} transitionModifiers - * @param {number} events - * @param {?function(string):string} mutation - * @this {Keyboard} - */ - - Keyboard.prototype.dispatchEventsForKeystroke = function dispatchEventsForKeystroke(keystroke, target, transitionModifiers = true, events = KeyEvents.ALL, mutation = null) { - if (transitionModifiers) { - this.dispatchModifierStateTransition(target, 0, keystroke.modifiers, events); - } + return event; +}; + +/** + * Fires the correct sequence of events on the given target as if the given + * action was undertaken by a human. + * + * @param {string} action e.g. "alt+shift+left" or "backspace" + * @param {!HTMLElement} target + * @param {?function()} callback + * @this {Keyboard} + */ +Keyboard.prototype.dispatchEventsForAction = function(action, target, callback) { + const keystroke = this.keystrokeForAction(action); + scheduleTask(50, () => this.dispatchEventsForKeystroke(keystroke, target)); + if (callback) { + scheduleTask(100, callback); + } +}; + +/** + * Fires the correct sequence of events on the given target as if the given + * input had been typed by a human. + * + * @param {string} input + * @param {!HTMLElement} target + * @param {?function()} callback + * @this {Keyboard} + */ +Keyboard.prototype.dispatchEventsForInput = function(input, target, callback) { + let currentModifierState = 0; + for (let i = 0, _length = input.length; i < _length; i++) { + const keystroke = this.keystrokeForCharCode(input.charCodeAt(i)); + scheduleTask(30, ((currentModifierState, keystrokeModifiers) => + this.dispatchModifierStateTransition(target, currentModifierState, keystrokeModifiers)) + .bind(this, currentModifierState, keystroke.modifiers)); + scheduleTask(20, ((keystroke, char) => + this.dispatchEventsForKeystroke(keystroke, target, false, KeyEvents.ALL, appender(char))) + .bind(this, keystroke, input[i])); + currentModifierState = keystroke.modifiers; + } + scheduleTask(20, () => this.dispatchModifierStateTransition(target, currentModifierState, 0)); + if (callback) { + scheduleTask(100, callback); + } +}; + +/** + * Fires the correct sequence of events on the given target as if the given + * keystroke was performed by a human. When simulating, for example, typing + * the letter "A" (assuming a U.S. English keyboard) then the sequence will + * look like this: + * + * keydown keyCode=16 (SHIFT) charCode=0 shiftKey=true + * keydown keyCode=65 (A) charCode=0 shiftKey=true + * keypress keyCode=65 (A) charCode=65 (A) shiftKey=true + * input data=A + * keyup keyCode=65 (A) charCode=0 shiftKey=true + * keyup keyCode=16 (SHIFT) charCode=0 shiftKey=false + * + * If the keystroke would not cause a character to be input, such as when + * pressing alt+shift+left, the sequence looks like this: + * + * keydown keyCode=16 (SHIFT) charCode=0 altKey=false shiftKey=true + * keydown keyCode=18 (ALT) charCode=0 altKey=true shiftKey=true + * keydown keyCode=37 (LEFT) charCode=0 altKey=true shiftKey=true + * keyup keyCode=37 (LEFT) charCode=0 altKey=true shiftKey=true + * keyup keyCode=18 (ALT) charCode=0 altKey=false shiftKey=true + * keyup keyCode=16 (SHIFT) charCode=0 altKey=false shiftKey=false + * + * To disable handling of modifier keys, call with `transitionModifiers` set + * to false. Doing so will omit the keydown and keyup events associated with + * shift, ctrl, alt, and meta keys surrounding the actual keystroke. + * + * @param {!Keystroke} keystroke + * @param {!HTMLElement} target + * @param {boolean=} transitionModifiers + * @param {number} events + * @param {?function(string):string} mutation + * @this {Keyboard} + */ + +Keyboard.prototype.dispatchEventsForKeystroke = function dispatchEventsForKeystroke(keystroke, target, transitionModifiers = true, events = KeyEvents.ALL, mutation = null) { + if (transitionModifiers) { + this.dispatchModifierStateTransition(target, 0, keystroke.modifiers, events); + } - const dispatchEvent = function (e) { - if (dirac.DEBUG_KEYSIM) { - console.log('event dispatch', e.keyCode, e.type, e); - } - const res = target.dispatchEvent(e); - if (dirac.DEBUG_KEYSIM) { - console.log(' => (event dispatch) ', res); - } - return res; - }; + const dispatchEvent = function(e) { + // if (dirac.DEBUG_KEYSIM) { + // // eslint-disable-next-line no-console + // console.log('event dispatch', e.keyCode, e.type, e); + // } + const res = target.dispatchEvent(e); + // if (dirac.DEBUG_KEYSIM) { + // // eslint-disable-next-line no-console + // console.log(' => (event dispatch) ', res); + // } + return res; + }; - let keydownEvent = undefined; - if (events & KeyEvents.DOWN) { - keydownEvent = this.createEventFromKeystroke('keydown', keystroke, target); - } + let keydownEvent = undefined; + if (events & KeyEvents.DOWN) { + keydownEvent = this.createEventFromKeystroke('keydown', keystroke, target); + } - if (keydownEvent && dispatchEvent(keydownEvent) && this.targetCanReceiveTextInput(target)) { - let keypressEvent = undefined; - if (events & KeyEvents.PRESS) { - keypressEvent = this.createEventFromKeystroke('keypress', keystroke, target); - } - if (keypressEvent && (keypressEvent.charCode || mutation || keystroke.mutation) && dispatchEvent(keypressEvent)) { - if (events & KeyEvents.INPUT) { - const inputEvent = this.createEventFromKeystroke('input', keystroke, target); - // CodeMirror does read input content back, so we have to add real content into target element - // we currently only support cursor at the end of input, no selection changes, etc. - const effectiveMutation = mutation || keystroke.mutation; - if (effectiveMutation) { - const newValue = effectiveMutation(target.value); - if (dirac.DEBUG_KEYSIM) { - console.log('mutation of value', target.value, newValue, target); - } - target.value = newValue; - } - dispatchEvent(inputEvent); - } + if (keydownEvent && dispatchEvent(keydownEvent) && this.targetCanReceiveTextInput(target)) { + let keypressEvent = undefined; + if (events & KeyEvents.PRESS) { + keypressEvent = this.createEventFromKeystroke('keypress', keystroke, target); + } + if (keypressEvent && (keypressEvent.charCode || mutation || keystroke.mutation) && dispatchEvent(keypressEvent)) { + if (events & KeyEvents.INPUT) { + const inputEvent = this.createEventFromKeystroke('input', keystroke, target); + // CodeMirror does read input content back, so we have to add real content into target element + // we currently only support cursor at the end of input, no selection changes, etc. + const effectiveMutation = mutation || keystroke.mutation; + if (effectiveMutation) { + const newValue = effectiveMutation(target.value); + // if (dirac.DEBUG_KEYSIM) { + // // eslint-disable-next-line no-console + // console.log('mutation of value', target.value, newValue, target); + // } + target.value = newValue; } + dispatchEvent(inputEvent); } + } + } - if (events & KeyEvents.UP) { - const keyupEvent = this.createEventFromKeystroke('keyup', keystroke, target); - dispatchEvent(keyupEvent); - } - - if (transitionModifiers) { - this.dispatchModifierStateTransition(target, keystroke.modifiers, 0); - } - }; - - /** - * Transitions from one modifier state to another by dispatching key events. - * - * @param {!EventTarget} target - * @param {number} fromModifierState - * @param {number} toModifierState - * @param {number} events - * @this {Keyboard} - * @private - */ - - Keyboard.prototype.dispatchModifierStateTransition = function dispatchModifierStateTransition(target, fromModifierState, toModifierState) { - const events = arguments.length <= 3 || arguments[3] === undefined ? KeyEvents.ALL : arguments[3]; - - let currentModifierState = fromModifierState; - const didHaveMeta = (fromModifierState & META) === META; - const willHaveMeta = (toModifierState & META) === META; - const didHaveCtrl = (fromModifierState & CTRL) === CTRL; - const willHaveCtrl = (toModifierState & CTRL) === CTRL; - const didHaveShift = (fromModifierState & SHIFT) === SHIFT; - const willHaveShift = (toModifierState & SHIFT) === SHIFT; - const didHaveAlt = (fromModifierState & ALT) === ALT; - const willHaveAlt = (toModifierState & ALT) === ALT; - - const includeKeyUp = events & KeyEvents.UP; - const includeKeyPress = events & KeyEvents.PRESS; - const includeKeyDown = events & KeyEvents.DOWN; - - const dispatchEvent = function (e) { - // console.log("dispatch", e); - return target.dispatchEvent(e); - }; - - if (includeKeyUp && didHaveMeta === true && willHaveMeta === false) { - // Release the meta key. - currentModifierState &= ~META; - dispatchEvent(this.createEventFromKeystroke('keyup', new Keystroke(currentModifierState, this._actionMap.META.keyCode), target)); - } - - if (includeKeyUp && didHaveCtrl === true && willHaveCtrl === false) { - // Release the ctrl key. - currentModifierState &= ~CTRL; - dispatchEvent(this.createEventFromKeystroke('keyup', new Keystroke(currentModifierState, this._actionMap.CTRL.keyCode), target)); - } - - if (includeKeyUp && didHaveShift === true && willHaveShift === false) { - // Release the shift key. - currentModifierState &= ~SHIFT; - dispatchEvent(this.createEventFromKeystroke('keyup', new Keystroke(currentModifierState, this._actionMap.SHIFT.keyCode), target)); - } + if (events & KeyEvents.UP) { + const keyupEvent = this.createEventFromKeystroke('keyup', keystroke, target); + dispatchEvent(keyupEvent); + } - if (includeKeyUp && didHaveAlt === true && willHaveAlt === false) { - // Release the alt key. - currentModifierState &= ~ALT; - dispatchEvent(this.createEventFromKeystroke('keyup', new Keystroke(currentModifierState, this._actionMap.ALT.keyCode), target)); - } + if (transitionModifiers) { + this.dispatchModifierStateTransition(target, keystroke.modifiers, 0); + } +}; + +/** + * Transitions from one modifier state to another by dispatching key events. + * + * @param {!EventTarget} target + * @param {number} fromModifierState + * @param {number} toModifierState + * @param {number} events + * @this {Keyboard} + * @private + */ + +Keyboard.prototype.dispatchModifierStateTransition = function dispatchModifierStateTransition(target, fromModifierState, toModifierState) { + const events = arguments.length <= 3 || arguments[3] === undefined ? KeyEvents.ALL : arguments[3]; + + let currentModifierState = fromModifierState; + const didHaveMeta = (fromModifierState & META) === META; + const willHaveMeta = (toModifierState & META) === META; + const didHaveCtrl = (fromModifierState & CTRL) === CTRL; + const willHaveCtrl = (toModifierState & CTRL) === CTRL; + const didHaveShift = (fromModifierState & SHIFT) === SHIFT; + const willHaveShift = (toModifierState & SHIFT) === SHIFT; + const didHaveAlt = (fromModifierState & ALT) === ALT; + const willHaveAlt = (toModifierState & ALT) === ALT; + + const includeKeyUp = events & KeyEvents.UP; + const includeKeyDown = events & KeyEvents.DOWN; + + const dispatchEvent = function(e) { + // console.log("dispatch", e); + return target.dispatchEvent(e); + }; - if (includeKeyDown && didHaveMeta === false && willHaveMeta === true) { - // Press the meta key. - currentModifierState |= META; - dispatchEvent(this.createEventFromKeystroke('keydown', new Keystroke(currentModifierState, this._actionMap.META.keyCode), target)); - } + if (includeKeyUp && didHaveMeta === true && willHaveMeta === false) { + // Release the meta key. + currentModifierState &= ~META; + dispatchEvent(this.createEventFromKeystroke('keyup', new Keystroke(currentModifierState, this._actionMap.META.keyCode), target)); + } - if (includeKeyDown && didHaveCtrl === false && willHaveCtrl === true) { - // Press the ctrl key. - currentModifierState |= CTRL; - dispatchEvent(this.createEventFromKeystroke('keydown', new Keystroke(currentModifierState, this._actionMap.CTRL.keyCode), target)); - } + if (includeKeyUp && didHaveCtrl === true && willHaveCtrl === false) { + // Release the ctrl key. + currentModifierState &= ~CTRL; + dispatchEvent(this.createEventFromKeystroke('keyup', new Keystroke(currentModifierState, this._actionMap.CTRL.keyCode), target)); + } - if (includeKeyDown && didHaveShift === false && willHaveShift === true) { - // Press the shift key. - currentModifierState |= SHIFT; - dispatchEvent(this.createEventFromKeystroke('keydown', new Keystroke(currentModifierState, this._actionMap.SHIFT.keyCode), target)); - } + if (includeKeyUp && didHaveShift === true && willHaveShift === false) { + // Release the shift key. + currentModifierState &= ~SHIFT; + dispatchEvent(this.createEventFromKeystroke('keyup', new Keystroke(currentModifierState, this._actionMap.SHIFT.keyCode), target)); + } - if (includeKeyDown && didHaveAlt === false && willHaveAlt === true) { - // Press the alt key. - currentModifierState |= ALT; - dispatchEvent(this.createEventFromKeystroke('keydown', new Keystroke(currentModifierState, this._actionMap.ALT.keyCode), target)); - } + if (includeKeyUp && didHaveAlt === true && willHaveAlt === false) { + // Release the alt key. + currentModifierState &= ~ALT; + dispatchEvent(this.createEventFromKeystroke('keyup', new Keystroke(currentModifierState, this._actionMap.ALT.keyCode), target)); + } - if (currentModifierState !== toModifierState) { - throw new Error('internal error, expected modifier state: ' + toModifierState + (', got: ' + currentModifierState)); - } - }; - - /** - * Returns the keystroke associated with the given action. - * - * @param {string} action - * @return {?Keystroke} - * @this {Keyboard} - */ - - Keyboard.prototype.keystrokeForAction = function keystrokeForAction(action) { - let keyCode = null; - let modifiers = 0; - let mutation = null; - - const parts = action.split('+'); - const lastPart = parts.pop(); - - parts.forEach(function (part) { - switch (part.toUpperCase()) { - case 'CTRL': - modifiers |= CTRL; - break; - case 'META': - modifiers |= META; - break; - case 'ALT': - modifiers |= ALT; - break; - case 'SHIFT': - modifiers |= SHIFT; - break; - default: - throw new Error('in "' + action + '", invalid modifier: ' + part); - } - }); - - const actionLookup = this._actionMap[lastPart.toUpperCase()]; - if (actionLookup) { - keyCode = actionLookup.keyCode; - mutation = actionLookup.mutation; - } else if (lastPart.length === 1) { - const lastPartKeystroke = this.keystrokeForCharCode(lastPart.charCodeAt(0)); - modifiers |= lastPartKeystroke.modifiers; - keyCode = lastPartKeystroke.keyCode; - } else { - throw new Error('in "' + action + '", invalid action: ' + lastPart); - } + if (includeKeyDown && didHaveMeta === false && willHaveMeta === true) { + // Press the meta key. + currentModifierState |= META; + dispatchEvent(this.createEventFromKeystroke('keydown', new Keystroke(currentModifierState, this._actionMap.META.keyCode), target)); + } - return new Keystroke(modifiers, keyCode, mutation); - }; - - /** - * Gets the keystroke used to generate the given character code. - * - * @param {number} charCode - * @return {?Keystroke} - * @this {Keyboard} - */ - Keyboard.prototype.keystrokeForCharCode = function keystrokeForCharCode(charCode) { - return this._charCodeKeyCodeMap[charCode]; - }; - - /** - * @param {!EventTarget} target - * @private - */ - Keyboard.prototype.targetCanReceiveTextInput = function targetCanReceiveTextInput(target) { - if (!target) { - return false; - } + if (includeKeyDown && didHaveCtrl === false && willHaveCtrl === true) { + // Press the ctrl key. + currentModifierState |= CTRL; + dispatchEvent(this.createEventFromKeystroke('keydown', new Keystroke(currentModifierState, this._actionMap.CTRL.keyCode), target)); + } - switch (target.nodeName && target.nodeName.toLowerCase()) { - case 'input': - const type = target.type; - return !(type === 'hidden' || type === 'radio' || type === 'checkbox'); + if (includeKeyDown && didHaveShift === false && willHaveShift === true) { + // Press the shift key. + currentModifierState |= SHIFT; + dispatchEvent(this.createEventFromKeystroke('keydown', new Keystroke(currentModifierState, this._actionMap.SHIFT.keyCode), target)); + } - case 'textarea': - return true; + if (includeKeyDown && didHaveAlt === false && willHaveAlt === true) { + // Press the alt key. + currentModifierState |= ALT; + dispatchEvent(this.createEventFromKeystroke('keydown', new Keystroke(currentModifierState, this._actionMap.ALT.keyCode), target)); + } - default: - return false; - } - }; - - return Keyboard; - })(); - - const US_ENGLISH_CHARCODE_KEYCODE_MAP = { - 32: new Keystroke(0, 32), // - 33: new Keystroke(SHIFT, 49), // ! - 34: new Keystroke(SHIFT, 222), // " - 35: new Keystroke(SHIFT, 51), // # - 36: new Keystroke(SHIFT, 52), // $ - 37: new Keystroke(SHIFT, 53), // % - 38: new Keystroke(SHIFT, 55), // & - 39: new Keystroke(0, 222), // ' - 40: new Keystroke(SHIFT, 57), // ( - 41: new Keystroke(SHIFT, 48), // ) - 42: new Keystroke(SHIFT, 56), // * - 43: new Keystroke(SHIFT, 187), // + - 44: new Keystroke(0, 188), // , - 45: new Keystroke(0, 189), // - - 46: new Keystroke(0, 190), // . - 47: new Keystroke(0, 191), // / - 48: new Keystroke(0, 48), // 0 - 49: new Keystroke(0, 49), // 1 - 50: new Keystroke(0, 50), // 2 - 51: new Keystroke(0, 51), // 3 - 52: new Keystroke(0, 52), // 4 - 53: new Keystroke(0, 53), // 5 - 54: new Keystroke(0, 54), // 6 - 55: new Keystroke(0, 55), // 7 - 56: new Keystroke(0, 56), // 8 - 57: new Keystroke(0, 57), // 9 - 58: new Keystroke(SHIFT, 186), // : - 59: new Keystroke(0, 186), // ; - 60: new Keystroke(SHIFT, 188), // < - 61: new Keystroke(0, 187), // = - 62: new Keystroke(SHIFT, 190), // > - 63: new Keystroke(SHIFT, 191), // ? - 64: new Keystroke(SHIFT, 50), // @ - 65: new Keystroke(SHIFT, 65), // A - 66: new Keystroke(SHIFT, 66), // B - 67: new Keystroke(SHIFT, 67), // C - 68: new Keystroke(SHIFT, 68), // D - 69: new Keystroke(SHIFT, 69), // E - 70: new Keystroke(SHIFT, 70), // F - 71: new Keystroke(SHIFT, 71), // G - 72: new Keystroke(SHIFT, 72), // H - 73: new Keystroke(SHIFT, 73), // I - 74: new Keystroke(SHIFT, 74), // J - 75: new Keystroke(SHIFT, 75), // K - 76: new Keystroke(SHIFT, 76), // L - 77: new Keystroke(SHIFT, 77), // M - 78: new Keystroke(SHIFT, 78), // N - 79: new Keystroke(SHIFT, 79), // O - 80: new Keystroke(SHIFT, 80), // P - 81: new Keystroke(SHIFT, 81), // Q - 82: new Keystroke(SHIFT, 82), // R - 83: new Keystroke(SHIFT, 83), // S - 84: new Keystroke(SHIFT, 84), // T - 85: new Keystroke(SHIFT, 85), // U - 86: new Keystroke(SHIFT, 86), // V - 87: new Keystroke(SHIFT, 87), // W - 88: new Keystroke(SHIFT, 88), // X - 89: new Keystroke(SHIFT, 89), // Y - 90: new Keystroke(SHIFT, 90), // Z - 91: new Keystroke(0, 219), // [ - 92: new Keystroke(0, 220), // \ - 93: new Keystroke(0, 221), // ] - 96: new Keystroke(0, 192), // ` - 97: new Keystroke(0, 65), // a - 98: new Keystroke(0, 66), // b - 99: new Keystroke(0, 67), // c - 100: new Keystroke(0, 68), // d - 101: new Keystroke(0, 69), // e - 102: new Keystroke(0, 70), // f - 103: new Keystroke(0, 71), // g - 104: new Keystroke(0, 72), // h - 105: new Keystroke(0, 73), // i - 106: new Keystroke(0, 74), // j - 107: new Keystroke(0, 75), // k - 108: new Keystroke(0, 76), // l - 109: new Keystroke(0, 77), // m - 110: new Keystroke(0, 78), // n - 111: new Keystroke(0, 79), // o - 112: new Keystroke(0, 80), // p - 113: new Keystroke(0, 81), // q - 114: new Keystroke(0, 82), // r - 115: new Keystroke(0, 83), // s - 116: new Keystroke(0, 84), // t - 117: new Keystroke(0, 85), // u - 118: new Keystroke(0, 86), // v - 119: new Keystroke(0, 87), // w - 120: new Keystroke(0, 88), // x - 121: new Keystroke(0, 89), // y - 122: new Keystroke(0, 90), // z - 123: new Keystroke(SHIFT, 219), // { - 124: new Keystroke(SHIFT, 220), // | - 125: new Keystroke(SHIFT, 221), // } - 126: new Keystroke(SHIFT, 192) // ~ - }; + if (currentModifierState !== toModifierState) { + throw new Error('internal error, expected modifier state: ' + toModifierState + (', got: ' + currentModifierState)); + } +}; + +/** + * Returns the keystroke associated with the given action. + * + * @param {string} action + * @return {?Keystroke} + * @this {Keyboard} + */ + +Keyboard.prototype.keystrokeForAction = function keystrokeForAction(action) { + let keyCode = null; + let modifiers = 0; + let mutation = null; + + const parts = action.split('+'); + const lastPart = parts.pop(); + + parts.forEach(function(part) { + switch (part.toUpperCase()) { + case 'CTRL': + modifiers |= CTRL; + break; + case 'META': + modifiers |= META; + break; + case 'ALT': + modifiers |= ALT; + break; + case 'SHIFT': + modifiers |= SHIFT; + break; + default: + throw new Error('in "' + action + '", invalid modifier: ' + part); + } + }); + + const actionLookup = this._actionMap[lastPart.toUpperCase()]; + if (actionLookup) { + keyCode = actionLookup.keyCode; + mutation = actionLookup.mutation; + } else if (lastPart.length === 1) { + const lastPartKeystroke = this.keystrokeForCharCode(lastPart.charCodeAt(0)); + modifiers |= lastPartKeystroke.modifiers; + keyCode = lastPartKeystroke.keyCode; + } else { + throw new Error('in "' + action + '", invalid action: ' + lastPart); + } - const US_ENGLISH_ACTION_MAP = { - BACKSPACE: {keyCode: 8, mutation: deleter(1)}, - TAB: {keyCode: 9, mutation: appender('\t')}, - ENTER: {keyCode: 13, mutation: appender('\n')}, - SHIFT: {keyCode: 16}, - CTRL: {keyCode: 17}, - ALT: {keyCode: 18}, - PAUSE: {keyCode: 19}, - CAPSLOCK: {keyCode: 20}, - ESCAPE: {keyCode: 27}, - SPACE: {keyCode: 32, mutation: appender(' ')}, - PAGEUP: {keyCode: 33}, - PAGEDOWN: {keyCode: 34}, - END: {keyCode: 35}, - HOME: {keyCode: 36}, - LEFT: {keyCode: 37}, - UP: {keyCode: 38}, - RIGHT: {keyCode: 39}, - DOWN: {keyCode: 40}, - INSERT: {keyCode: 45}, - DELETE: {keyCode: 46}, - META: {keyCode: 91}, - F1: {keyCode: 112}, - F2: {keyCode: 113}, - F3: {keyCode: 114}, - F4: {keyCode: 115}, - F5: {keyCode: 116}, - F6: {keyCode: 117}, - F7: {keyCode: 118}, - F8: {keyCode: 119}, - F9: {keyCode: 120}, - F10: {keyCode: 121}, - F11: {keyCode: 122}, - F12: {keyCode: 123} - }; + return new Keystroke(modifiers, keyCode, mutation); +}; + +/** + * Gets the keystroke used to generate the given character code. + * + * @param {number} charCode + * @return {?Keystroke} + * @this {Keyboard} + */ +Keyboard.prototype.keystrokeForCharCode = function keystrokeForCharCode(charCode) { + return this._charCodeKeyCodeMap[charCode]; +}; + +/** + * @param {!EventTarget} target + * @private + */ +Keyboard.prototype.targetCanReceiveTextInput = function targetCanReceiveTextInput(target) { + if (!target) { + return false; + } - /** - * Gets a keyboard instance configured as a U.S. English keyboard would be. - * - * @return {!Keyboard} - */ - Keyboard.US_ENGLISH = new Keyboard(US_ENGLISH_CHARCODE_KEYCODE_MAP, US_ENGLISH_ACTION_MAP); + switch (target.nodeName && target.nodeName.toLowerCase()) { + case 'input': + return !(target.type === 'hidden' || target.type === 'radio' || target.type === 'checkbox'); - exports.KeyEvents = KeyEvents; - exports.Keystroke = Keystroke; - exports.Keyboard = Keyboard; + case 'textarea': + return true; -}); + default: + return false; + } +}; + +const US_ENGLISH_CHARCODE_KEYCODE_MAP = { + 32: new Keystroke(0, 32), // + 33: new Keystroke(SHIFT, 49), // ! + 34: new Keystroke(SHIFT, 222), // " + 35: new Keystroke(SHIFT, 51), // # + 36: new Keystroke(SHIFT, 52), // $ + 37: new Keystroke(SHIFT, 53), // % + 38: new Keystroke(SHIFT, 55), // & + 39: new Keystroke(0, 222), // ' + 40: new Keystroke(SHIFT, 57), // ( + 41: new Keystroke(SHIFT, 48), // ) + 42: new Keystroke(SHIFT, 56), // * + 43: new Keystroke(SHIFT, 187), // + + 44: new Keystroke(0, 188), // , + 45: new Keystroke(0, 189), // - + 46: new Keystroke(0, 190), // . + 47: new Keystroke(0, 191), // / + 48: new Keystroke(0, 48), // 0 + 49: new Keystroke(0, 49), // 1 + 50: new Keystroke(0, 50), // 2 + 51: new Keystroke(0, 51), // 3 + 52: new Keystroke(0, 52), // 4 + 53: new Keystroke(0, 53), // 5 + 54: new Keystroke(0, 54), // 6 + 55: new Keystroke(0, 55), // 7 + 56: new Keystroke(0, 56), // 8 + 57: new Keystroke(0, 57), // 9 + 58: new Keystroke(SHIFT, 186), // : + 59: new Keystroke(0, 186), // ; + 60: new Keystroke(SHIFT, 188), // < + 61: new Keystroke(0, 187), // = + 62: new Keystroke(SHIFT, 190), // > + 63: new Keystroke(SHIFT, 191), // ? + 64: new Keystroke(SHIFT, 50), // @ + 65: new Keystroke(SHIFT, 65), // A + 66: new Keystroke(SHIFT, 66), // B + 67: new Keystroke(SHIFT, 67), // C + 68: new Keystroke(SHIFT, 68), // D + 69: new Keystroke(SHIFT, 69), // E + 70: new Keystroke(SHIFT, 70), // F + 71: new Keystroke(SHIFT, 71), // G + 72: new Keystroke(SHIFT, 72), // H + 73: new Keystroke(SHIFT, 73), // I + 74: new Keystroke(SHIFT, 74), // J + 75: new Keystroke(SHIFT, 75), // K + 76: new Keystroke(SHIFT, 76), // L + 77: new Keystroke(SHIFT, 77), // M + 78: new Keystroke(SHIFT, 78), // N + 79: new Keystroke(SHIFT, 79), // O + 80: new Keystroke(SHIFT, 80), // P + 81: new Keystroke(SHIFT, 81), // Q + 82: new Keystroke(SHIFT, 82), // R + 83: new Keystroke(SHIFT, 83), // S + 84: new Keystroke(SHIFT, 84), // T + 85: new Keystroke(SHIFT, 85), // U + 86: new Keystroke(SHIFT, 86), // V + 87: new Keystroke(SHIFT, 87), // W + 88: new Keystroke(SHIFT, 88), // X + 89: new Keystroke(SHIFT, 89), // Y + 90: new Keystroke(SHIFT, 90), // Z + 91: new Keystroke(0, 219), // [ + 92: new Keystroke(0, 220), // \ + 93: new Keystroke(0, 221), // ] + 96: new Keystroke(0, 192), // ` + 97: new Keystroke(0, 65), // a + 98: new Keystroke(0, 66), // b + 99: new Keystroke(0, 67), // c + 100: new Keystroke(0, 68), // d + 101: new Keystroke(0, 69), // e + 102: new Keystroke(0, 70), // f + 103: new Keystroke(0, 71), // g + 104: new Keystroke(0, 72), // h + 105: new Keystroke(0, 73), // i + 106: new Keystroke(0, 74), // j + 107: new Keystroke(0, 75), // k + 108: new Keystroke(0, 76), // l + 109: new Keystroke(0, 77), // m + 110: new Keystroke(0, 78), // n + 111: new Keystroke(0, 79), // o + 112: new Keystroke(0, 80), // p + 113: new Keystroke(0, 81), // q + 114: new Keystroke(0, 82), // r + 115: new Keystroke(0, 83), // s + 116: new Keystroke(0, 84), // t + 117: new Keystroke(0, 85), // u + 118: new Keystroke(0, 86), // v + 119: new Keystroke(0, 87), // w + 120: new Keystroke(0, 88), // x + 121: new Keystroke(0, 89), // y + 122: new Keystroke(0, 90), // z + 123: new Keystroke(SHIFT, 219), // { + 124: new Keystroke(SHIFT, 220), // | + 125: new Keystroke(SHIFT, 221), // } + 126: new Keystroke(SHIFT, 192) // ~ +}; + +const US_ENGLISH_ACTION_MAP = { + BACKSPACE: {keyCode: 8, mutation: deleter(1)}, + TAB: {keyCode: 9, mutation: appender('\t')}, + ENTER: {keyCode: 13, mutation: appender('\n')}, + SHIFT: {keyCode: 16}, + CTRL: {keyCode: 17}, + ALT: {keyCode: 18}, + PAUSE: {keyCode: 19}, + CAPSLOCK: {keyCode: 20}, + ESCAPE: {keyCode: 27}, + SPACE: {keyCode: 32, mutation: appender(' ')}, + PAGEUP: {keyCode: 33}, + PAGEDOWN: {keyCode: 34}, + END: {keyCode: 35}, + HOME: {keyCode: 36}, + LEFT: {keyCode: 37}, + UP: {keyCode: 38}, + RIGHT: {keyCode: 39}, + DOWN: {keyCode: 40}, + INSERT: {keyCode: 45}, + DELETE: {keyCode: 46}, + META: {keyCode: 91}, + F1: {keyCode: 112}, + F2: {keyCode: 113}, + F3: {keyCode: 114}, + F4: {keyCode: 115}, + F5: {keyCode: 116}, + F6: {keyCode: 117}, + F7: {keyCode: 118}, + F8: {keyCode: 119}, + F9: {keyCode: 120}, + F10: {keyCode: 121}, + F11: {keyCode: 122}, + F12: {keyCode: 123} +}; + +/** + * Gets a keyboard instance configured as a U.S. English keyboard would be. + * + * @return {!Keyboard} + */ +Keyboard.US_ENGLISH = new Keyboard(US_ENGLISH_CHARCODE_KEYCODE_MAP, US_ENGLISH_ACTION_MAP); + +export {KeyEvents, Keystroke, Keyboard}; diff --git a/front_end/dirac/module.json b/front_end/dirac/module.json index 1a8689ab292..02245fd5822 100644 --- a/front_end/dirac/module.json +++ b/front_end/dirac/module.json @@ -1,8 +1,6 @@ { "dependencies": [ - "platform", - "common", - "host" + "common" ], "modules": [ "dirac.js", @@ -11,10 +9,11 @@ "keysim.js" ], "scripts": [ - "require-implant.js" ], "skip_compilation": [ - "require-implant.js" + "parinfer.js", + "parinfer-codemirror.js", + "keysim.js" ], "resources": [ ] diff --git a/front_end/dirac/require-implant.js b/front_end/dirac/require-implant.js deleted file mode 100644 index 72d59c03636..00000000000 --- a/front_end/dirac/require-implant.js +++ /dev/null @@ -1,32 +0,0 @@ -// @ts-nocheck -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -if (typeof runtime !== 'undefined') { - // this code runs only in dev mode - // we want to avoid tweaking inspector.html - (function (d, script) { - const insertScript = function (url, f) { - script = d.createElement('script'); - script.type = 'text/javascript'; - script.async = true; - if (f) { - script.onload = f; - } - script.src = url; - d.getElementsByTagName('head')[0].appendChild(script); - }; - - insertScript('dirac/.compiled/implant/goog/base.js', function () { - goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', true); - goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING = true; - insertScript('dirac/.compiled/implant/goog/deps.js', function () { - insertScript('dirac/.compiled/implant/cljs_deps.js', function () { - goog.require('dirac.devtools'); - goog.require('dirac.implant'); - }); - }); - }); - })(document); -} diff --git a/front_end/dirac_lazy/dirac_lazy.js b/front_end/dirac_lazy/dirac_lazy.js deleted file mode 100644 index 16d4288d4d0..00000000000 --- a/front_end/dirac_lazy/dirac_lazy.js +++ /dev/null @@ -1,974 +0,0 @@ -// @ts-nocheck -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -console.log('dirac-lazy module import!'); - -if (!window.dirac) { - console.error('window.dirac was expected to exist when loading dirac_lazy overlay'); - throw new Error('window.dirac was expected to exist when loading dirac_lazy overlay'); -} - -Object.assign(window.dirac, (function () { - - const namespacesSymbolsCache = new Map(); - - // --- eval support ----------------------------------------------------------------------------------------------------- - - function lookupCurrentContext() { - return self.UI.context.flavor(SDK.ExecutionContext); - } - - function evalInContext(context, code, silent, callback) { - if (!context) { - console.warn('Requested evalInContext with null context:', code); - return; - } - const resultCallback = function (result, exceptionDetails) { - if (dirac.DEBUG_EVAL) { - console.log('evalInContext/resultCallback: result', result, 'exceptionDetails', exceptionDetails); - } - if (callback) { - let exceptionDescription = null; - if (exceptionDetails) { - const exception = exceptionDetails.exception; - if (exception) { - exceptionDescription = exception.description; - } - if (!exceptionDescription) { - exceptionDescription = exceptionDetails.text; - } - if (!exceptionDescription) { - exceptionDescription = '?'; - } - } - - callback(result, exceptionDescription); - } - }; - try { - if (dirac.DEBUG_EVAL) { - console.log('evalInContext', context, silent, code); - } - context.evaluate({ - expression: code, - objectGroup: 'console', - includeCommandLineAPI: true, - silent: silent, - returnByValue: true, - generatePreview: false - }, false, false).then(answer => resultCallback(answer.object, answer.exceptionDetails)); - } catch (e) { - console.error('failed js evaluation in context:', context, 'code', code); - } - } - - function hasCurrentContext() { - return !!lookupCurrentContext(); - } - - function evalInCurrentContext(code, silent, callback) { - if (dirac.DEBUG_EVAL) { - console.log('evalInCurrentContext called:', code, silent, callback); - } - evalInContext(lookupCurrentContext(), code, silent, callback); - } - - function lookupDefaultContext() { - if (dirac.DEBUG_EVAL) { - console.log('lookupDefaultContext called'); - } - if (!SDK.targetManager) { - if (dirac.DEBUG_EVAL) { - console.log(' !SDK.targetManager => bail out'); - } - return null; - } - const target = SDK.targetManager.mainTarget(); - if (!target) { - if (dirac.DEBUG_EVAL) { - console.log(' !target => bail out'); - } - return null; - } - const runtimeModel = target.model(SDK.RuntimeModel); - if (!runtimeModel) { - if (dirac.DEBUG_EVAL) { - console.log(' !runtimeModel => bail out'); - } - return null; - } - const executionContexts = runtimeModel.executionContexts(); - if (dirac.DEBUG_EVAL) { - console.log(' execution contexts:', executionContexts); - } - for (let i = 0; i < executionContexts.length; ++i) { - const executionContext = executionContexts[i]; - if (executionContext.isDefault) { - if (dirac.DEBUG_EVAL) { - console.log(' execution context #' + i + ' isDefault:', executionContext); - } - return executionContext; - } - } - if (executionContexts.length > 0) { - if (dirac.DEBUG_EVAL) { - console.log(' lookupDefaultContext failed to find valid context => return the first one'); - } - return executionContexts[0]; - } - if (dirac.DEBUG_EVAL) { - console.log(' lookupDefaultContext failed to find valid context => no context avail'); - } - return null; - } - - function hasDefaultContext() { - return !!lookupDefaultContext(); - } - - function evalInDefaultContext(code, silent, callback) { - if (dirac.DEBUG_EVAL) { - console.log('evalInDefaultContext called:', code, silent, callback); - } - evalInContext(lookupDefaultContext(), code, silent, callback); - } - - function getMainDebuggerModel() { - return SDK.targetManager.mainTarget().model(SDK.DebuggerModel); - } - - const debuggerEventsUnsubscribers = new Map(); - - /** - * @return {boolean} - */ - function subscribeDebuggerEvents(callback) { - if (debuggerEventsUnsubscribers.has(callback)) { - throw new Error('subscribeDebuggerEvents called without prior unsubscribeDebuggerEvents for callback ' + callback); - } - const globalObjectClearedHandler = (...args) => { - callback('GlobalObjectCleared', ...args); - }; - const debuggerPausedHandler = (...args) => { - callback('DebuggerPaused', ...args); - }; - const debuggerResumedHandler = (...args) => { - callback('DebuggerResumed', ...args); - }; - - SDK.targetManager.addModelListener(SDK.DebuggerModel, SDK.DebuggerModel.Events.GlobalObjectCleared, globalObjectClearedHandler, window.dirac); - SDK.targetManager.addModelListener(SDK.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, debuggerPausedHandler, window.dirac); - SDK.targetManager.addModelListener(SDK.DebuggerModel, SDK.DebuggerModel.Events.DebuggerResumed, debuggerResumedHandler, window.dirac); - - debuggerEventsUnsubscribers.set(callback, () => { - SDK.targetManager.removeModelListener(SDK.DebuggerModel, SDK.DebuggerModel.Events.GlobalObjectCleared, globalObjectClearedHandler, window.dirac); - SDK.targetManager.removeModelListener(SDK.DebuggerModel, SDK.DebuggerModel.Events.DebuggerPaused, debuggerPausedHandler, window.dirac); - SDK.targetManager.removeModelListener(SDK.DebuggerModel, SDK.DebuggerModel.Events.DebuggerResumed, debuggerResumedHandler, window.dirac); - return true; - }); - - return true; - } - - /** - * @return {boolean} - */ - function unsubscribeDebuggerEvents(callback) { - if (!debuggerEventsUnsubscribers.has(callback)) { - throw new Error('unsubscribeDebuggerEvents called without prior subscribeDebuggerEvents for callback ' + callback); - } - - const unsubscriber = debuggerEventsUnsubscribers.get(callback); - debuggerEventsUnsubscribers.delete(callback); - return unsubscriber(); - } - - // --- console ---------------------------------------------------------------------------------------------------------- - - function addConsoleMessageToMainTarget(type, level, text, parameters) { - const target = SDK.targetManager.mainTarget(); - if (!target) { - console.warn('Unable to add console message to main target (no target): ', text); - return; - } - const runtimeModel = target.model(SDK.RuntimeModel); - if (!runtimeModel) { - console.warn('Unable to add console message to main target (no runtime model): ', text); - return; - } - const sanitizedText = text || ''; - const msg = new SDK.ConsoleMessage(runtimeModel, SDK.ConsoleMessage.MessageSource.Other, level, - sanitizedText, type, undefined, undefined, undefined, parameters); - SDK.consoleModel.addMessage(msg); - } - - function evaluateCommandInConsole(contextName, code) { - const context = contextName === 'current' ? lookupCurrentContext() : lookupDefaultContext(); - if (!context) { - console.warn("evaluateCommandInConsole got null '" + contextName + "' context:", code); - return; - } - const commandMessage = new SDK.ConsoleMessage(context.runtimeModel, SDK.ConsoleMessage.MessageSource.JS, null, code, SDK.ConsoleMessage.MessageType.Command); - commandMessage.setExecutionContextId(context.id); - commandMessage.skipHistory = true; - SDK.consoleModel.evaluateCommandInConsole(context, commandMessage, code, false); - } - - // --- scope info ------------------------------------------------------------------------------------------------------- - - function getScopeTitle(scope) { - let title = null; - - switch (scope.type()) { - case Protocol.Debugger.ScopeType.Local: - title = Common.UIString('Local'); - break; - case Protocol.Debugger.ScopeType.Closure: - const scopeName = scope.name(); - if (scopeName) { - title = Common.UIString('Closure (%s)', UI.beautifyFunctionName(scopeName)); - } else { - title = Common.UIString('Closure'); - } - break; - case Protocol.Debugger.ScopeType.Catch: - title = Common.UIString('Catch'); - break; - case Protocol.Debugger.ScopeType.Block: - title = Common.UIString('Block'); - break; - case Protocol.Debugger.ScopeType.Script: - title = Common.UIString('Script'); - break; - case Protocol.Debugger.ScopeType.With: - title = Common.UIString('With Block'); - break; - case Protocol.Debugger.ScopeType.Global: - title = Common.UIString('Global'); - break; - } - - return title; - } - - function extractNamesFromScopePromise(scope) { - const title = getScopeTitle(scope); - const remoteObject = Sources.SourceMapNamesResolver.resolveScopeInObject(scope); - - const result = {title: title}; - let resolved = false; - - return new Promise(function (resolve) { - - function processProperties(answer) { - const properties = answer.properties; - if (properties) { - result.props = properties.map(function (property) { - const propertyRecord = {name: property.name}; - if (property.resolutionSourceProperty) { - const identifier = property.resolutionSourceProperty.name; - if (identifier !== property.name) { - propertyRecord.identifier = identifier; - } - } - return propertyRecord; - }); - } - - resolved = true; - resolve(result); - } - - function timeoutProperties() { - if (resolved) { - return; - } - console.warn('Unable to retrieve properties from remote object', remoteObject); - resolve(result); - } - - remoteObject.getAllProperties(false, false).then(processProperties); - setTimeout(timeoutProperties, dirac.REMOTE_OBJECT_PROPERTIES_FETCH_TIMEOUT); - }); - } - - function extractScopeInfoFromScopeChainAsync(callFrame) { - if (!callFrame) { - return Promise.resolve(null); - } - - return new Promise(function (resolve) { - const scopeNamesPromises = []; - - const scopeChain = callFrame.scopeChain(); - for (let i = 0; i < scopeChain.length; ++i) { - const scope = scopeChain[i]; - if (scope.type() === Protocol.Debugger.ScopeType.Global) { - continue; - } - - scopeNamesPromises.unshift(extractNamesFromScopePromise(scope)); - } - - Promise.all(scopeNamesPromises).then(function (frames) { - const result = {frames: frames}; - resolve(result); - }); - }); - } - - // --- helpers ---------------------------------------------------------------------------------------------------------- - - /** - * @param {string} namespaceName - * @return {function(string)} - */ - function prepareUrlMatcher(namespaceName) { - // shadow-cljs uses slightly different convention to output files - // for example given namespaceName 'my.cool.ns' - // standard clojurescript outputs into directory structure $some-prefix/my/cool/ns.js - // cljs files are placed under the same names - // - // shadow-cljs outputs into flat directory structure cljs-runtime/my.cool.ns.js - // but shadow-cljs maintains tree-like structure for original cljs sources, similar to standard - // - const relativeNSPathStandard = dirac.nsToRelpath(namespaceName, 'js'); - const relativeNSPathShadow = relativeNSPathStandard.replace('/', '.'); - const parser = document.createElement('a'); - return /** @suppressGlobalPropertiesCheck */ function (url) { - parser.href = url; - // console.log("URL MATCH", relativeNSPathShadow, parser.pathname); - return parser.pathname.endsWith(relativeNSPathStandard) || parser.pathname.endsWith(relativeNSPathShadow); - }; - } - - function unique(a) { - return Array.from(new Set(a)); - } - - function isRelevantSourceCode(uiSourceCode) { - return uiSourceCode.contentType().isScript() && !uiSourceCode.contentType().isFromSourceMap() && - uiSourceCode.project().type() === Workspace.projectTypes.Network; - } - - function getRelevantSourceCodes(workspace) { - return workspace.uiSourceCodes().filter(isRelevantSourceCode); - } - - // --- parsing namespaces ----------------------------------------------------------------------------------------------- - - /** - * @param {string} url - * @param {string} cljsSourceCode - * @return {!Array} - */ - function parseClojureScriptNamespaces(url, cljsSourceCode) { - if (dirac.DEBUG_CACHES) { - console.groupCollapsed("parseClojureScriptNamespaces: " + url); - console.log(cljsSourceCode); - console.groupEnd(); - } - if (!cljsSourceCode) { - console.warn('unexpected empty source from ' + url); - return []; - } - const descriptor = dirac.parseNsFromSource(cljsSourceCode); - if (!descriptor) { - return []; - } - - descriptor.url = url; - return [descriptor]; - } - - /** - * @param {string} url - * @param {?string} jsSourceCode - * @return {!Array} - */ - function parsePseudoNamespaces(url, jsSourceCode) { - if (dirac.DEBUG_CACHES) { - console.groupCollapsed("parsePseudoNamespaces: " + url); - console.log(jsSourceCode); - console.groupEnd(); - } - if (!jsSourceCode) { - console.warn('unexpected empty source from ' + url); - return []; - } - - const result = []; - // standard clojurescript emits: goog.provide('goog.something'); - // shadow-cljs emits: goog.module("goog.something"); - const re = /goog\.(provide|module)\(['"](.*?)['"]\);/gm; - let m; - while (m = re.exec(jsSourceCode)) { - const namespaceName = m[2]; - const descriptor = { - name: namespaceName, - url: url, - pseudo: true - }; - result.push(descriptor); - } - - return result; - } - - function ensureSourceMapLoadedAsync(script) { - if (!script.sourceMapURL) { - return Promise.resolve(null); - } - const sourceMap = Bindings.debuggerWorkspaceBinding.sourceMapForScript(script); - if (sourceMap) { - return Promise.resolve(sourceMap); - } - return new Promise(resolve => { - let counter = 0; - const interval = setInterval(() => { - const sourceMap = Bindings.debuggerWorkspaceBinding.sourceMapForScript(script); - if (sourceMap) { - clearInterval(interval); - resolve(sourceMap); - } - counter += 1; - if (counter > 100) { // 10s - clearInterval(interval); - console.warn("source map didn't load in time for", script); - resolve(null); - } - }, 100); - }); - } - - /** - * @param {!SDK.Script} script - * @return {!Promise>} - * @suppressGlobalPropertiesCheck - */ - function parseNamespacesDescriptorsAsync(script) { - if (script.isContentScript()) { - return Promise.resolve([]); - } - - // I assume calling maybeLoadSourceMap is no longer needed, source maps are loaded lazily when referenced - // Bindings.debuggerWorkspaceBinding.maybeLoadSourceMap(script); - return ensureSourceMapLoadedAsync(script).then(/** @suppressGlobalPropertiesCheck */sourceMap => { - const scriptUrl = script.contentURL(); - const promises = []; - let realNamespace = false; - if (sourceMap) { - for (const url of sourceMap.sourceURLs()) { - // take only .cljs or .cljc urls, make sure url params and fragments get matched properly - // examples: - // http://localhost:9977/.compiled/demo/clojure/browser/event.cljs?rel=1463085025939 - // http://localhost:9977/.compiled/demo/dirac_sample/demo.cljs?rel=1463085026941 - const parser = document.createElement('a'); - parser.href = url; - if (parser.pathname.match(/\.clj.$/)) { - const contentProvider = sourceMap.sourceContentProvider(url, Common.resourceTypes.SourceMapScript); - const namespaceDescriptorsPromise = contentProvider.requestContent().then(cljsSourceCode => parseClojureScriptNamespaces(scriptUrl, cljsSourceCode.content)); - promises.push(namespaceDescriptorsPromise); - realNamespace = true; - } - } - } - - // we are also interested in pseudo namespaces from google closure library - if (!realNamespace) { - const parser = document.createElement('a'); - parser.href = scriptUrl; - if (parser.pathname.match(/\.js$/)) { - const namespaceDescriptorsPromise = script.requestContent().then(jsSourceCode => parsePseudoNamespaces(scriptUrl, jsSourceCode.content)); - promises.push(namespaceDescriptorsPromise); - } - } - - const concatResults = results => { - return [].concat.apply([], results); - }; - - return Promise.all(promises).then(concatResults); - }); - } - - // --- namespace names -------------------------------------------------------------------------------------------------- - - function getMacroNamespaceNames(namespaces) { - let names = []; - for (const descriptor of Object.values(namespaces)) { - if (!descriptor.detectedMacroNamespaces) { - continue; - } - names = names.concat(descriptor.detectedMacroNamespaces); - } - return dirac.deduplicate(names); - } - - function getSourceCodeNamespaceDescriptorsAsync(uiSourceCode) { - if (!uiSourceCode) { - return Promise.resolve([]); - } - const script = getScriptFromSourceCode(uiSourceCode); - if (!script) { - return Promise.resolve([]); - } - // noinspection JSCheckFunctionSignatures - return parseNamespacesDescriptorsAsync(script); - } - - function prepareNamespacesFromDescriptors(namespaceDescriptors) { - const result = {}; - for (const descriptor of namespaceDescriptors) { - result[descriptor.name] = descriptor; - } - return result; - } - - function extractNamespacesAsyncWorker() { - const workspace = Workspace.workspace; - if (!workspace) { - console.error('unable to locate Workspace when extracting all ClojureScript namespace names'); - return Promise.resolve([]); - } - - const uiSourceCodes = getRelevantSourceCodes(workspace); - const promises = []; - if (dirac.DEBUG_CACHES) { - console.log('extractNamespacesAsyncWorker initial processing of ' + uiSourceCodes.length + ' source codes'); - } - for (const uiSourceCode of uiSourceCodes) { - const namespaceDescriptorsPromise = getSourceCodeNamespaceDescriptorsAsync(uiSourceCode); - promises.push(namespaceDescriptorsPromise); - } - - const concatResults = results => { - return [].concat.apply([], results); - }; - - return Promise.all(promises).then(concatResults); - } - - let extractNamespacesAsyncInFlightPromise = null; - - function extractNamespacesAsync() { - // extractNamespacesAsync can take some time parsing all namespaces - // it could happen that extractNamespacesAsync() is called multiple times from code-completion code - // here we cache in-flight promise to prevent that - if (extractNamespacesAsyncInFlightPromise) { - return extractNamespacesAsyncInFlightPromise; - } - - if (dirac.namespacesCache) { - return Promise.resolve(dirac.namespacesCache); - } - - dirac.namespacesCache = {}; - startListeningForWorkspaceChanges(); - - extractNamespacesAsyncInFlightPromise = extractNamespacesAsyncWorker().then(descriptors => { - const newDescriptors = prepareNamespacesFromDescriptors(descriptors); - const newDescriptorsCount = Object.keys(newDescriptors).length; - if (!dirac.namespacesCache) { - dirac.namespacesCache = {}; - } - Object.assign(dirac.namespacesCache, newDescriptors); - const allDescriptorsCount = Object.keys(dirac.namespacesCache).length; - if (dirac.DEBUG_CACHES) { - console.log('extractNamespacesAsync finished namespacesCache with ' + newDescriptorsCount + ' items ' + - '(' + allDescriptorsCount + ' in total)'); - } - dirac.reportNamespacesCacheMutation(); - return dirac.namespacesCache; - }); - - extractNamespacesAsyncInFlightPromise.then(result => extractNamespacesAsyncInFlightPromise = null); - return extractNamespacesAsyncInFlightPromise; - } - - function invalidateNamespacesCache() { - if (dirac.DEBUG_CACHES) { - console.log('invalidateNamespacesCache'); - } - dirac.namespacesCache = null; - } - - function extractSourceCodeNamespacesAsync(uiSourceCode) { - if (!isRelevantSourceCode(uiSourceCode)) { - return Promise.resolve({}); - } - - return getSourceCodeNamespaceDescriptorsAsync(uiSourceCode).then(prepareNamespacesFromDescriptors); - } - - function extractAndMergeSourceCodeNamespacesAsync(uiSourceCode) { - if (!isRelevantSourceCode(uiSourceCode)) { - console.warn('extractAndMergeSourceCodeNamespacesAsync called on irrelevant source code', uiSourceCode); - return; - } - - if (dirac.DEBUG_CACHES) { - console.log('extractAndMergeSourceCodeNamespacesAsync', uiSourceCode); - } - const jobs = [extractNamespacesAsync(), extractSourceCodeNamespacesAsync(uiSourceCode)]; - return Promise.all(jobs).then(([namespaces, result]) => { - const addedNamespaceNames = Object.keys(result); - if (addedNamespaceNames.length) { - Object.assign(namespaces, result); - if (dirac.DEBUG_CACHES) { - console.log('updated namespacesCache by merging ', addedNamespaceNames, - 'from', uiSourceCode.contentURL(), - ' => new namespaces count:', Object.keys(namespaces).length); - } - dirac.reportNamespacesCacheMutation(); - } - return result; - }); - } - - function removeNamespacesMatchingUrl(url) { - extractNamespacesAsync().then(namespaces => { - const removedNames = []; - for (const namespaceName of Object.keys(namespaces)) { - const descriptor = namespaces[namespaceName]; - if (descriptor.url === url) { - delete namespaces[namespaceName]; - removedNames.push(namespaceName); - } - } - - if (dirac.DEBUG_CACHES) { - console.log('removeNamespacesMatchingUrl removed ' + removedNames.length + ' namespaces for url: ' + url + - ' new namespaces count:' + Object.keys(namespaces).length); - } - }); - } - - // --- namespace symbols ------------------------------------------------------------------------------------------------ - - /** - * @param {!Array} uiSourceCodes - * @param {function(string)} urlMatcherFn - * @return {!Array} - */ - function findMatchingSourceCodes(uiSourceCodes, urlMatcherFn) { - const matching = []; - for (let i = 0; i < uiSourceCodes.length; i++) { - const uiSourceCode = uiSourceCodes[i]; - if (urlMatcherFn(uiSourceCode.url())) { - matching.push(uiSourceCode); - } - } - return matching; - } - - /** - * @param {!Array} names - * @param {string} namespaceName - * @return {!Array} - */ - function filterNamesForNamespace(names, namespaceName) { - const prefix = namespaceName + '/'; - const prefixLength = prefix.length; - - return names.filter(name => name.startsWith(prefix)).map(name => name.substring(prefixLength)); - } - - /** - * @param {!Workspace.UISourceCode} uiSourceCode - * @return {?SDK.Script} - */ - function getScriptFromSourceCode(uiSourceCode) { - const target = SDK.targetManager.mainTarget(); - if (!target) { - throw new Error( - 'getScriptFromSourceCode called when there is no main target\n' + - `uiSourceCode: name=${uiSourceCode.name()} url=${uiSourceCode.url()} project=${uiSourceCode.project().type()}\n`); - } - const debuggerModel = /** @type {!SDK.DebuggerModel} */ (target.model(SDK.DebuggerModel)); - if (!debuggerModel) { - throw new Error( - `getScriptFromSourceCode called when main target has no debuggerModel target=${target}\n` + - `uiSourceCode: name=${uiSourceCode.name()} url=${uiSourceCode.url()} project=${uiSourceCode.project().type()}\n`); - } - const scriptFile = Bindings.debuggerWorkspaceBinding.scriptFile(uiSourceCode, debuggerModel); - if (!scriptFile) { - // do not treat missing script file as a fatal error, only log error into internal dirac console - // see https://github.com/binaryage/dirac/issues/79 - - // disabled to prevent console spam - if (dirac.DEBUG_CACHES) { - console.error( - 'uiSourceCode expected to have scriptFile associated\n' + - `uiSourceCode: name=${uiSourceCode.name()} url=${uiSourceCode.url()} project=${uiSourceCode.project().type()}\n`); - } - return null; - } - const script = scriptFile.getScript(); - if (!script) { - throw new Error( - 'uiSourceCode expected to have _script associated\n' + - `uiSourceCode: name=${uiSourceCode.name()} url=${uiSourceCode.url()} project=${uiSourceCode.project().type()}\n`); - } - if (!(script instanceof SDK.Script)) { - throw new Error( - 'getScriptFromSourceCode expected to return an instance of SDK.Script\n' + - `uiSourceCode: name=${uiSourceCode.name()} url=${uiSourceCode.url()} project=${uiSourceCode.project().type()}\n`); - } - return script; - } - - function extractNamesFromSourceMap(uiSourceCode, namespaceName) { - const script = getScriptFromSourceCode(uiSourceCode); - if (!script) { - console.error("unable to locate script when extracting symbols for ClojureScript namespace '" + namespaceName + "'"); - return []; - } - const sourceMap = Bindings.debuggerWorkspaceBinding.sourceMapForScript(/** @type {!SDK.Script} */(script)); - if (!sourceMap) { - console.error("unable to locate sourceMap when extracting symbols for ClojureScript namespace '" + namespaceName + "'"); - return []; - } - const payload = sourceMap.payload(); - if (!payload) { - console.error("unable to locate payload when extracting symbols for ClojureScript namespace '" + namespaceName + "'"); - return []; - } - return payload.names || []; - } - - function extractNamespaceSymbolsAsyncWorker(namespaceName) { - const workspace = Workspace.workspace; - if (!workspace) { - console.error("unable to locate Workspace when extracting symbols for ClojureScript namespace '" + namespaceName + "'"); - return Promise.resolve([]); - } - - return new Promise(resolve => { - const urlMatcherFn = prepareUrlMatcher(namespaceName); - const uiSourceCodes = getRelevantSourceCodes(workspace); - - // not there may be multiple matching sources for given namespaceName - // figwheel reloading is just adding new files and not removing old ones - const matchingSourceCodes = findMatchingSourceCodes(uiSourceCodes, urlMatcherFn); - if (!matchingSourceCodes.length) { - if (dirac.DEBUG_CACHES) { - console.warn("cannot find any matching source file for ClojureScript namespace '" + namespaceName + "'"); - } - resolve([]); - return; - } - - // we simply extract names from all matching source maps and then we filter them to match our namespace name and - // deduplicate them - const results = []; - for (const uiSourceCode of matchingSourceCodes) { - results.push(extractNamesFromSourceMap(uiSourceCode, namespaceName)); - } - const allNames = [].concat.apply([], results); - const filteredNames = unique(filterNamesForNamespace(allNames, namespaceName)); - - if (dirac.DEBUG_CACHES) { - console.log('extracted ' + filteredNames.length + ' symbol names for namespace', namespaceName, matchingSourceCodes.map(i => i.url())); - } - - resolve(filteredNames); - }); - } - - function extractNamespaceSymbolsAsync(namespaceName) { - if (!namespaceName) { - return Promise.resolve([]); - } - - if (namespacesSymbolsCache.has(namespaceName)) { - return namespacesSymbolsCache.get(namespaceName); - } - - const promisedResult = extractNamespaceSymbolsAsyncWorker(namespaceName); - - namespacesSymbolsCache.set(namespaceName, promisedResult); - - startListeningForWorkspaceChanges(); - return promisedResult; - } - - function invalidateNamespaceSymbolsCache(namespaceName = null) { - if (dirac.DEBUG_CACHES) { - console.log('invalidateNamespaceSymbolsCache', namespaceName); - } - if (namespaceName) { - namespacesSymbolsCache.delete(namespaceName); - } else { - namespacesSymbolsCache.clear(); - } - } - - // --- macro namespaces symbols ----------------------------------------------------------------------------------------- - // - // a situation is a bit more tricky here - // we don't have source mapping to clojure land in case of macro .clj files (makes no sense) - // but thanks to our access to all existing (ns ...) forms in the project we can infer at least some information - // we can at least collect macro symbols referred to via :refer - - function extractMacroNamespaceSymbolsAsyncWorker(namespaceName) { - - const collectMacroSymbols = namespaceDescriptors => { - const symbols = []; - for (const descriptor of Object.values(namespaceDescriptors)) { - const refers = descriptor.macroRefers; - if (!refers) { - continue; - } - for (const symbol of Object.keys(refers)) { - const ns = refers[symbol]; - if (ns === namespaceName) { - symbols.push(symbol); - } - } - } - return dirac.deduplicate(symbols); - }; - - return dirac.extractNamespacesAsync().then(collectMacroSymbols); - } - - function extractMacroNamespaceSymbolsAsync(namespaceName) { - if (!namespaceName) { - return Promise.resolve([]); - } - - const promisedResult = extractMacroNamespaceSymbolsAsyncWorker(namespaceName); - - if (dirac.DEBUG_CACHES) { - promisedResult.then(result => { - console.log('extractMacroNamespaceSymbolsAsync resolved', namespaceName, result); - }); - } - - return promisedResult; - } - - // --- changes ---------------------------------------------------------------------------------------------------------- - // this is to reflect dynamically updated files e.g. by Figwheel - - let listeningForWorkspaceChanges = false; - - function invalidateNamespaceSymbolsMatchingUrl(url) { - for (const namespaceName of namespacesSymbolsCache.keys()) { - const matcherFn = prepareUrlMatcher(namespaceName); - if (matcherFn(url)) { - dirac.invalidateNamespaceSymbolsCache(namespaceName); - } - } - } - - function handleSourceCodeAdded(event) { - const uiSourceCode = event.data; - if (uiSourceCode && isRelevantSourceCode(uiSourceCode)) { - const url = uiSourceCode.url(); - if (dirac.DEBUG_WATCHING) { - console.log('handleSourceCodeAdded', url); - } - extractAndMergeSourceCodeNamespacesAsync(uiSourceCode); - invalidateNamespaceSymbolsMatchingUrl(url); - } - } - - function handleSourceCodeRemoved(event) { - const uiSourceCode = event.data; - if (uiSourceCode && isRelevantSourceCode(uiSourceCode)) { - const url = uiSourceCode.url(); - if (dirac.DEBUG_WATCHING) { - console.log('handleSourceCodeRemoved', url); - } - removeNamespacesMatchingUrl(url); - invalidateNamespaceSymbolsMatchingUrl(url); - } - } - - function startListeningForWorkspaceChanges() { - if (listeningForWorkspaceChanges) { - return; - } - - if (dirac.DEBUG_WATCHING) { - console.log('startListeningForWorkspaceChanges'); - } - - const workspace = Workspace.workspace; - if (!workspace) { - console.error('unable to locate Workspace in startListeningForWorkspaceChanges'); - return; - } - - workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, handleSourceCodeAdded, dirac); - workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeRemoved, handleSourceCodeRemoved, dirac); - - listeningForWorkspaceChanges = true; - } - - function stopListeningForWorkspaceChanges() { - if (!listeningForWorkspaceChanges) { - return; - } - - if (dirac.DEBUG_WATCHING) { - console.log('stopListeningForWorkspaceChanges'); - } - - const workspace = Workspace.workspace; - if (!workspace) { - console.error('unable to locate Workspace in stopListeningForWorkspaceChanges'); - return; - } - - workspace.removeEventListener(Workspace.Workspace.Events.UISourceCodeAdded, handleSourceCodeAdded, dirac); - workspace.removeEventListener(Workspace.Workspace.Events.UISourceCodeRemoved, handleSourceCodeRemoved, dirac); - - listeningForWorkspaceChanges = false; - } - - function registerDiracLinkAction(action) { - if (Components.Linkifier.diracLinkHandlerAction) { - throw new Error('registerDiracLinkAction already set'); - } - Components.Linkifier.diracLinkHandlerAction = action; - } - - // --- exported interface ----------------------------------------------------------------------------------------------- - - // don't forget to update externs.js too - return { - lazyLoaded: true, - namespacesSymbolsCache: namespacesSymbolsCache, - namespacesCache: null, - REMOTE_OBJECT_PROPERTIES_FETCH_TIMEOUT: 1000, - lookupCurrentContext: lookupCurrentContext, - evalInCurrentContext: evalInCurrentContext, - hasCurrentContext: hasCurrentContext, - evalInDefaultContext: evalInDefaultContext, - hasDefaultContext: hasDefaultContext, - getMainDebuggerModel: getMainDebuggerModel, - subscribeDebuggerEvents: subscribeDebuggerEvents, - unsubscribeDebuggerEvents: unsubscribeDebuggerEvents, - addConsoleMessageToMainTarget: addConsoleMessageToMainTarget, - evaluateCommandInConsole: evaluateCommandInConsole, - startListeningForWorkspaceChanges: startListeningForWorkspaceChanges, - stopListeningForWorkspaceChanges: stopListeningForWorkspaceChanges, - extractScopeInfoFromScopeChainAsync: extractScopeInfoFromScopeChainAsync, - extractNamespaceSymbolsAsync: extractNamespaceSymbolsAsync, - invalidateNamespaceSymbolsCache: invalidateNamespaceSymbolsCache, - extractMacroNamespaceSymbolsAsync: extractMacroNamespaceSymbolsAsync, - extractNamespacesAsync: extractNamespacesAsync, - invalidateNamespacesCache: invalidateNamespacesCache, - getMacroNamespaceNames: getMacroNamespaceNames, - registerDiracLinkAction: registerDiracLinkAction - - }; - -})()); - -console.log('dirac-lazy module imported!'); diff --git a/front_end/dirac_lazy/module.json b/front_end/dirac_lazy/module.json deleted file mode 100644 index 565450558de..00000000000 --- a/front_end/dirac_lazy/module.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "dependencies": [ - "sources", - "components", - "dirac" - ], - "modules": [ - "dirac_lazy.js" - ], - "scripts": [ - ], - "skip_compilation": [ - ], - "resources": [ - ] -} diff --git a/front_end/externs.js b/front_end/externs.js index d682659d234..006915b1b1f 100644 --- a/front_end/externs.js +++ b/front_end/externs.js @@ -322,303 +322,6 @@ diff_match_patch.prototype = { diff_cleanupSemantic(diff) {} }; -const dirac = { - /** @type {boolean} */ - DEBUG_EVAL: true, - /** @type {boolean} */ - hasFeature: true, - /** @type {boolean} */ - hasREPL: true, - /** @type {boolean} */ - hasParinfer: true, - /** @type {boolean} */ - hasFriendlyLocals: true, - /** @type {boolean} */ - hasClusteredLocals: true, - /** @type {boolean} */ - hasInlineCFs: true, - /** @type {boolean} */ - hasWelcomeMessage: true, - /** @type {boolean} */ - hasCleanUrls: true, - /** @type {boolean} */ - hasBeautifyFunctionNames: true, - /** @type {boolean} */ - hasLinkActions: true, - /** @type {?Object.} */ - namespacesCache: null, - - /** - * @param {string} name - * @return {boolean} - */ - getToggle: function (name) {}, - - /** - * @param {string} name - * @param {*} value - */ - setToggle: function (name, value) {}, - /** - * @return {!Promise} - */ - getReadyPromise: function () {}, - /** - * @param {string} code - * @return {string} - */ - codeAsString: function(code) {}, - /** - * @param {string} string - * @return {string} - */ - stringEscape: function(string) {}, - /** - * @param {string} action - */ - dispatchEventsForAction: function(action) {}, - /** - * @param {Node} node - * @param {string} query - */ - querySelectionAllDeep: function(node, query) {}, - lookupCurrentContext: function() {}, - /** - * @param {string} code - * @param {boolean} silent - * @param {?} callback - */ - evalInCurrentContext: function(code, silent, callback) {}, - /** - * @param {string} code - * @param {boolean} silent - * @param {?} callback - */ - evalInDefaultContext: function(code, silent, callback) {}, - /** - * @return {boolean} - */ - hasCurrentContext: function() {}, - /** - * @return {boolean} - */ - hasDefaultContext: function() {}, - - /** - * @return {?} - */ - getMainDebuggerModel: function() {}, - /** - * @param {?} callback - * @return {boolean} - * @this {Object} - */ - subscribeDebuggerEvents: function(callback) {}, - /** - * @param {?} callback - * @return {boolean} - */ - unsubscribeDebuggerEvents: function(callback) {}, - - /** - * @param {?} callFrame - * @return {!Promise} - */ - extractScopeInfoFromScopeChainAsync: function(callFrame) {}, - /** - * @param {string} namespaceName - * @return {!Promise>} - */ - extractNamespaceSymbolsAsync: function(namespaceName) {}, - /** - * @param {string} namespaceName - * @return {!Promise>} - */ - extractMacroNamespaceSymbolsAsync: function(namespaceName) {}, - /** - * @return {!Promise>} - */ - extractNamespacesAsync: function() {}, - - startListeningForWorkspaceChanges: function() {}, - stopListeningForWorkspaceChanges: function() {}, - /** - * @param {string=} namespaceName - */ - invalidateNamespaceSymbolsCache: function(namespaceName) {}, - invalidateNamespacesCache: function() {}, - - /** - * @param {Object.} namespaces - * @return {Array.} - */ - getMacroNamespaceNames: function(namespaces) {}, - - /** - * @param {!Object} action - */ - registerDiracLinkAction: function(action) {}, - - /** - * @param {Array.} coll - * @param {function(T):string=} keyFn - * @return {Array.} - * @template T - */ - deduplicate: function(coll, keyFn) {}, - - /** - * @param {Array.} array - * @param {function(T, T):number} comparator - * @return {Array.} - * @template T - */ - stableSort: function(array, comparator) {}, - - /** - * @param {string=} namespaceName - * @return {?dirac.NamespaceDescriptor} - */ - getNamespace: function(namespaceName) {}, - - /** - * @param {string} type - * @param {string} level - * @param {string} text - * @param {Array.<*>=} parameters - */ - addConsoleMessageToMainTarget: function(type, level, text, parameters) {}, - - // -- these are dynamically added by dirac.implant namespace ------------------------------------------------------------ - - initConsole: function() {}, - initRepl: function() {}, - /** - * @param {string} panelId - */ - notifyPanelSwitch: function(panelId) {}, - notifyFrontendInitialized: function() {}, - getVersion: function() {}, - getRuntimeTag: function(f) {}, - /** - * @param {Element} textAreaElement - * @param {boolean} useParinfer - * @return {!CodeMirror} - */ - adoptPrompt: function(textAreaElement, useParinfer) {}, - /** - * @param {number} requestId - * @param {string} code - * @param {dirac.ScopeInfo} scopeInfo - */ - sendEvalRequest: function(requestId, code, scopeInfo) {}, - /** - * @param {string} ns - * @param {string} ext - * @return {string} - */ - nsToRelpath: function(ns, ext) {}, - - triggerInternalError: function() {}, - triggerInternalErrorInPromise: function() {}, - triggerInternalErrorAsErrorLog: function() {}, - /** - * @param {string} name - * @return {string} - */ - getFunctionName: function(name) {}, - - /** - * @param {string} name - * @return {string} - */ - getFullFunctionName: function(name) {}, - - /** - * @return {!Promise.>} - */ - getReplSpecialsAsync: function() {}, - - /** - * @param {string} source - * @return {?dirac.NamespaceDescriptor} - */ - parseNsFromSource: function(source) {}, - - /** - * @return {boolean} - * */ - isIntercomReady: function() { - }, - - reportNamespacesCacheMutation: function() {}, - - /** - * @param {string} text - */ - feedback: function(text) {} -}; - -/** - * @typedef {{name:!string, identifier:?string}} - */ -dirac.ScopeFrameProp; - -/** - * @typedef {{title:?string, props:?Array.}} - */ -dirac.ScopeFrame; - -/** - * @typedef {{frames:!Array.}} - */ -dirac.ScopeInfo; - -/** - * @typedef {{ - * name:!string, - * url:!string, - * pseudo:?boolean, - * namespaceAliases:?Object., - * namespaceRefers:?Object., - * macroNamespaceAliases:?Object., - * macroRefers:?Object., - * detectedMacroNamespaces:?Array. - * }} - */ -dirac.NamespaceDescriptor; - -const Keysim = {} - -/** @constructor */ -Keysim.Keyboard = function() {}; -Keysim.Keyboard.prototype = { - /** - * Fires the correct sequence of events on the given target as if the given - * action was undertaken by a human. - * - * @param {string} action e.g. "alt+shift+left" or "backspace" - * @param {Element} target - * @param {?function()} callback - */ - dispatchEventsForAction: function (action, target, callback) { - }, - - /** - * Fires the correct sequence of events on the given target as if the given - * input had been typed by a human. - * - * @param {string} input - * @param {Element} target - * @param {?function()} callback - */ - dispatchEventsForInput: function (input, target, callback) { - }, -}; - -/** @type {Keysim.Keyboard} */ -Keysim.Keyboard.US_ENGLISH; - /** @constructor */ const Doc = function() {}; Doc.prototype = { diff --git a/front_end/host/InspectorFrontendHost.js b/front_end/host/InspectorFrontendHost.js index ce679183bcb..687c37ef193 100644 --- a/front_end/host/InspectorFrontendHost.js +++ b/front_end/host/InspectorFrontendHost.js @@ -142,17 +142,17 @@ export class InspectorFrontendHostStub { * @suppressGlobalPropertiesCheck */ inspectedURLChanged(url) { - // @ts-ignore - const dirac = window["dirac"]; - if (!dirac.isIntercomReady()) { + // document.title = Common.UIString.UIString('DevTools - %s', url.replace(/^https?:\/\//, '')); + const diracAngel = Common.getDiracAngel(); + if (!diracAngel.isIntercomReady()) { // postpone this code, we use document.title for signalling of frontend loading completion, see inspector.js const that = this; setTimeout(function() { that.inspectedURLChanged(url); }, 500); return; } - const version = dirac.getVersion(); - dirac.getRuntimeTag( + const version = diracAngel.getVersion(); + diracAngel.getRuntimeTag( /** * @suppressGlobalPropertiesCheck * @param {string} tag diff --git a/front_end/main/MainImpl.js b/front_end/main/MainImpl.js index 9b90b17018e..5e031b4b520 100644 --- a/front_end/main/MainImpl.js +++ b/front_end/main/MainImpl.js @@ -1,4 +1,3 @@ -// @ts-nocheck // Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -91,6 +90,7 @@ export class MainImpl { Root.Runtime.Runtime.setPlatform(Host.Platform.platform()); Root.Runtime.Runtime.setL10nCallback(ls); await this.requestAndRegisterLocaleData(); + dirac.implant.init_implant(); Host.InspectorFrontendHost.InspectorFrontendHostInstance.getPreferences(this._gotPreferences.bind(this)); } @@ -113,7 +113,8 @@ export class MainImpl { _gotPreferences(prefs) { console.timeStamp('Main._gotPreferences'); // for dirac testing - if (Root.Runtime.queryParam('reset_settings')) { + if (Root.Runtime.Runtime.queryParam('reset_settings')) { + // eslint-disable-next-line no-console console.info('DIRAC TESTING: clear devtools settings because reset_settings is present in url params'); window.localStorage.clear(); // also wipe-out local storage to prevent tests flakiness prefs = {}; @@ -240,8 +241,6 @@ export class MainImpl { * @suppressGlobalPropertiesCheck */ async _createAppUI() { - await dirac.getReadyPromise(); - MainImpl.time('Main._createAppUI'); self.UI.viewManager = UI.ViewManager.ViewManager.instance(); @@ -378,7 +377,8 @@ export class MainImpl { // Allow UI cycles to repaint prior to creating connection. setTimeout(this._initializeTarget.bind(this), 0); MainImpl.timeEnd('Main._showAppUI'); - dirac.feedback('devtools ready'); + const diracAngel = Common.getDiracAngel(); + diracAngel.feedback('devtools ready'); } async _initializeTarget() { @@ -421,7 +421,8 @@ export class MainImpl { } this._lateInitDonePromise = Promise.all(promises); MainImpl.timeEnd('Main._lateInitialization'); - dirac.notifyFrontendInitialized(); + const diracAngel = Common.getDiracAngel(); + diracAngel.notifyFrontendInitialized(); } /** diff --git a/front_end/main/module.json b/front_end/main/module.json index d5a54926702..d7e28c3d8f0 100644 --- a/front_end/main/module.json +++ b/front_end/main/module.json @@ -542,7 +542,6 @@ "i18n", "platform", "sdk", - "dirac", "persistence" ], "modules": [ diff --git a/front_end/object_ui/ObjectPropertiesSection.js b/front_end/object_ui/ObjectPropertiesSection.js index a03f5452b39..f31aa04245e 100644 --- a/front_end/object_ui/ObjectPropertiesSection.js +++ b/front_end/object_ui/ObjectPropertiesSection.js @@ -143,7 +143,7 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow return 2; } const name = property.name; - if (name.indexOf('__') != -1) { + if (name.indexOf('__') !== -1) { return 1; } return 0; @@ -158,7 +158,8 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow * @return {number} */ static CompareProperties(propertyA, propertyB) { - if (dirac.hasClusteredLocals) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.hasClusteredLocals) { const clusterA = ObjectUI.ObjectPropertiesSection.PropertyCluster(propertyA); const clusterB = ObjectUI.ObjectPropertiesSection.PropertyCluster(propertyB); @@ -741,7 +742,7 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement { */ function getFriendlyName(name) { const duIndex = name.indexOf('__'); - if (duIndex != -1) { + if (duIndex !== -1) { return name.substring(0, duIndex); } const suMatch = name.match(/(.*?)_\d+$/); @@ -751,6 +752,7 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement { return null; } + const diracAngel = Common.getDiracAngel(); const friendlyNamesTable = {}; let previousProperty = null; const tailProperties = []; @@ -762,15 +764,15 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement { continue; } - if (dirac.hasClusteredLocals) { + if (diracAngel.toggles.hasClusteredLocals) { property._cluster = ObjectUI.ObjectPropertiesSection.PropertyCluster(property); - if (previousProperty && property._cluster != previousProperty._cluster) { + if (previousProperty && property._cluster !== previousProperty._cluster) { property._afterClusterBoundary = true; previousProperty._beforeClusterBoundary = true; } } - if (dirac.hasFriendlyLocals) { + if (diracAngel.toggles.hasFriendlyLocals) { const friendlyName = getFriendlyName(property.name); if (friendlyName) { property._friendlyName = friendlyName; @@ -1316,6 +1318,7 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement { } } + /** * @unrestricted */ diff --git a/front_end/protocol_client/module.json b/front_end/protocol_client/module.json index 2a07871f911..70162816fed 100644 --- a/front_end/protocol_client/module.json +++ b/front_end/protocol_client/module.json @@ -1,7 +1,6 @@ { "dependencies": [ "common", - "dirac", "host" ], "modules": [ diff --git a/front_end/sdk/ConsoleModel.js b/front_end/sdk/ConsoleModel.js index 7f9ab754f42..9f9dd300600 100644 --- a/front_end/sdk/ConsoleModel.js +++ b/front_end/sdk/ConsoleModel.js @@ -1,4 +1,3 @@ -// @ts-nocheck /* * Copyright (C) 2011 Google Inc. All rights reserved. * @@ -205,8 +204,9 @@ export class ConsoleModel extends Common.ObjectWrapper.ObjectWrapper { if (msg.parameters) { const firstParam = msg.parameters[0]; + // @ts-ignore if (firstParam && firstParam.value === '~~$DIRAC-MSG$~~') { - this.dispatchEventToListeners(SDK.ConsoleModel.Events.DiracMessage, msg); + this.dispatchEventToListeners(Events.DiracMessage, msg); return; } } diff --git a/front_end/sdk/RuntimeModel.js b/front_end/sdk/RuntimeModel.js index 922b0be0ce9..a4a89071049 100644 --- a/front_end/sdk/RuntimeModel.js +++ b/front_end/sdk/RuntimeModel.js @@ -78,16 +78,25 @@ export class RuntimeModel extends SDKModel { 'hasLinkActions' ]; + const moduleSetting = Common.Settings.moduleSetting; + const diracAngel = Common.getDiracAngel(); for (const flagName of flagNames) { - if (dirac.hostedInExtension) { + if (diracAngel.hostedInExtension) { // in hosted mode we receive flags via dirac_flags url param // we pass them down to moduleSetting - self.Common.moduleSetting(flagName).set(dirac.getToggle(flagName)); + moduleSetting(flagName).set(diracAngel.getToggle(flagName)); } else { // in internal mode we simply use flags from moduleSetting - dirac.setToggle(flagName, self.Common.moduleSetting(flagName).get()); + diracAngel.setToggle(flagName, moduleSetting(flagName).get()); } - self.Common.moduleSetting(flagName).addChangeListener(this._diracToggleChanged.bind(this, flagName)); + + /** + * @param {!Common.EventTarget.EventTargetEvent} event + */ + const handler = event => { + diracAngel.setToggle(flagName, event.data); + }; + moduleSetting(flagName).addChangeListener(handler); } } @@ -279,14 +288,6 @@ export class RuntimeModel extends SDKModel { this._agent.invoke_setCustomObjectFormatterEnabled({enabled}); } - /** - * @param {string} name - * @param {!Common.EventTarget.EventTargetEvent} event - */ - _diracToggleChanged(name, event) { - dirac.setToggle(name, event.data); - } - /** * @param {string} expression * @param {string} sourceURL diff --git a/front_end/sdk/SourceMap.js b/front_end/sdk/SourceMap.js index e817342f73c..ddeb2ac8a44 100644 --- a/front_end/sdk/SourceMap.js +++ b/front_end/sdk/SourceMap.js @@ -107,13 +107,6 @@ export class SourceMap { dispose() { } - - /** - * @return {?SourceMapV3} - */ - payload() { - return null; - } } // eslint-disable-next-line no-unused-vars @@ -287,14 +280,6 @@ export class TextSourceMap { return this._sourceMappingURL; } - /** - * @override - * @return {?SourceMapV3} - */ - payload() { - return this._payload; - } - /** * @override * @return {!Array.} @@ -732,14 +717,6 @@ export class WasmSourceMap { return WasmSourceMap.FAKE_URL; } - /** - * @override - * @return {?SourceMapV3} - */ - payload() { - return null; - } - /** * @override * @return {!Array.} diff --git a/front_end/sdk/module.json b/front_end/sdk/module.json index ab3922829a4..51f77d08af6 100644 --- a/front_end/sdk/module.json +++ b/front_end/sdk/module.json @@ -4,7 +4,6 @@ "host", "platform", "protocol_client", - "dirac", "text_utils" ], "extensions": [ diff --git a/front_end/shell.json b/front_end/shell.json index 813864485c2..9856c3eedb8 100644 --- a/front_end/shell.json +++ b/front_end/shell.json @@ -22,7 +22,6 @@ { "name": "ui", "type": "autostart" }, { "name": "workspace", "type": "autostart" }, - { "name": "dirac_lazy" }, { "name": "changes" }, { "name": "client_variations" }, { "name": "cm_modes" }, diff --git a/front_end/source_frame/SourcesTextEditor.js b/front_end/source_frame/SourcesTextEditor.js index 10c65287574..6d674e6516b 100644 --- a/front_end/source_frame/SourcesTextEditor.js +++ b/front_end/source_frame/SourcesTextEditor.js @@ -36,7 +36,7 @@ export class SourcesTextEditor extends TextEditor.CodeMirrorTextEditor.CodeMirro this._delegate = delegate; - if (dirac.hasInlineCFs) { + if (diracAngel.toggles.hasInlineCFs) { this.codeMirror().on('update', this._update.bind(this)); } this.codeMirror().on('cursorActivity', this._cursorActivity.bind(this)); diff --git a/front_end/sources/CallStackSidebarPane.js b/front_end/sources/CallStackSidebarPane.js index 9d69a10676e..4cd4e86204b 100644 --- a/front_end/sources/CallStackSidebarPane.js +++ b/front_end/sources/CallStackSidebarPane.js @@ -1,4 +1,3 @@ -// @ts-nocheck /* * Copyright (C) 2008 Apple Inc. All Rights Reserved. * @@ -238,15 +237,16 @@ export class CallStackSidebarPane extends UI.View.SimpleView { const title = element.createChild('div', 'call-frame-item-title'); const titleElement = title.createChild('div', 'call-frame-title-text'); titleElement.textContent = item.title; - if (dirac.hasBeautifyFunctionNames) { - if (item.functionName) { - titleElement.title = dirac.getFullFunctionName(item.functionName); - } - } if (item.isAsyncHeader) { element.classList.add('async-header'); } else { titleElement.title = item.title; + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.hasBeautifyFunctionNames) { + if (item.functionName) { + titleElement.title = diracAngel.getFullFunctionName(item.functionName); + } + } const linkElement = element.createChild('div', 'call-frame-location'); linkElement.textContent = item.linkText.trimMiddle(30); linkElement.title = item.linkText; diff --git a/front_end/sources/DebuggerPlugin.js b/front_end/sources/DebuggerPlugin.js index 34b720fb270..692bb4c407a 100644 --- a/front_end/sources/DebuggerPlugin.js +++ b/front_end/sources/DebuggerPlugin.js @@ -1169,7 +1169,7 @@ export class DebuggerPlugin extends Plugin { const value = info.value; const propertyCount = value.preview ? value.preview.properties.length : 0; const entryCount = value.preview && value.preview.entries ? value.preview.entries.length : 0; - if (dirac.hasInlineCFs && value.customPreview()) { + if (diracAngel.toggles.hasInlineCFs && value.customPreview()) { const customValueEl = (new ObjectUI.CustomPreviewComponent.CustomPreviewComponent(value)).element; nameValuePair.appendChild(customValueEl); } else if (value.preview && propertyCount + entryCount < 10) { diff --git a/front_end/ui/InspectorView.js b/front_end/ui/InspectorView.js index e92a7d3f98b..f557e5f9317 100644 --- a/front_end/ui/InspectorView.js +++ b/front_end/ui/InspectorView.js @@ -272,7 +272,8 @@ export class InspectorView extends VBox { * @param {boolean} focus */ _showDrawer(focus) { - dirac.feedback('showDrawer'); + const diracAngel = Common.getDiracAngel(); + diracAngel.feedback('showDrawer'); if (this._drawerTabbedPane.isShowing()) { return; } @@ -380,7 +381,8 @@ export class InspectorView extends VBox { */ _tabSelected(event) { const tabId = /** @type {string} */ (event.data['tabId']); - dirac.notifyPanelSwitch(tabId); + const diracAngel = Common.getDiracAngel(); + diracAngel.notifyPanelSwitch(tabId); Host.userMetrics.panelShown(tabId); } diff --git a/front_end/ui/TextPrompt.js b/front_end/ui/TextPrompt.js index 9d967d34b3f..fc3039c5425 100644 --- a/front_end/ui/TextPrompt.js +++ b/front_end/ui/TextPrompt.js @@ -52,6 +52,7 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper { this._proxyElementDisplay = 'inline-block'; this._autocompletionTimeout = DefaultAutocompletionTimeout; this._title = ''; + this._ignoreEnter = false; this._queryRange = null; this._previousText = ''; this._currentSuggestion = null; @@ -365,7 +366,7 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper { } break; } - if (!dirac.ignoreEnter) { + if (!this._ignoreEnter) { if (isEnterKey(event)) { event.preventDefault(); } @@ -774,13 +775,6 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper { selection.addRange(selectionRange); } - /** - * @return {string} - */ - getSuggestBoxRepresentation() { - return 'getSuggestBoxRepresentation not implemented for UI.TextPrompt'; - } - /** * @return {number} -1 if no caret can be found in text prompt */ diff --git a/front_end/ui/UIUtils.js b/front_end/ui/UIUtils.js index f71ee8ae7e5..2dfd60d5cac 100644 --- a/front_end/ui/UIUtils.js +++ b/front_end/ui/UIUtils.js @@ -1212,8 +1212,9 @@ export function initializeUIUtils(document, themeSetting) { * @return {string} */ export function beautifyFunctionName(name) { - if (dirac.hasBeautifyFunctionNames) { - return dirac.getFunctionName(name); + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.hasBeautifyFunctionNames) { + return diracAngel.getFunctionName(name); } return name || Common.UIString.UIString('(anonymous)'); } diff --git a/front_end/workspace/UISourceCode.js b/front_end/workspace/UISourceCode.js index 25a24ec206f..72f9ffa12a0 100644 --- a/front_end/workspace/UISourceCode.js +++ b/front_end/workspace/UISourceCode.js @@ -151,7 +151,8 @@ export class UISourceCode extends Common.ObjectWrapper.ObjectWrapper { name = decodeURI(name); } // @ts-ignore - if (dirac.hasCleanUrls) { + const diracAngel = Common.getDiracAngel(); + if (diracAngel.toggles.hasCleanUrls) { // strip all after ? in the name const qmarkIndex = name.indexOf('?'); if (qmarkIndex != -1) {