diff --git a/resources/unpacked/devtools/front_end/console/DiracPrompt.js b/resources/unpacked/devtools/front_end/console/DiracPrompt.js index 16fb70fc6c..b91666e288 100644 --- a/resources/unpacked/devtools/front_end/console/DiracPrompt.js +++ b/resources/unpacked/devtools/front_end/console/DiracPrompt.js @@ -260,11 +260,11 @@ WebInspector.DiracPromptWithHistory.prototype = { if (javascriptCompletion) { this._prefixRange = new WebInspector.TextRange(cursor.line, token.start + javascriptCompletion.offset, cursor.line, cursor.ch); const completionsForJavascriptReady = this._completionsForJavascriptReady.bind(this, this._lastAutocompleteRequest, !!reverse, !!force); - this._loadJavascriptCompletions(this._lastAutocompleteRequest, javascriptCompletion.prefix, force || false, completionsForJavascriptReady); + this._loadJavascriptCompletions(this._lastAutocompleteRequest, javascriptCompletion.prefix, !!force, completionsForJavascriptReady); } else { this._prefixRange = new WebInspector.TextRange(cursor.line, token.start, cursor.line, cursor.ch); const completionsForClojureScriptReady = this._completionsForClojureScriptReady.bind(this, this._lastAutocompleteRequest, !!reverse, !!force); - this._loadClojureScriptCompletions(this._lastAutocompleteRequest, prefix, force || false, completionsForClojureScriptReady); + this._loadClojureScriptCompletions(this._lastAutocompleteRequest, prefix, !!force, completionsForClojureScriptReady); } }, @@ -400,7 +400,7 @@ WebInspector.DiracPromptWithHistory.prototype = { return; } - const suggestStyle = (style = "") => `suggest-cljs ${style}`; + const makeSuggestStyle = (style = "") => `suggest-cljs ${style}`; const namespaceSelector = name => { return function(namespaceDescriptors) { @@ -431,13 +431,13 @@ WebInspector.DiracPromptWithHistory.prototype = { const annotateQualifiedSymbols = (style, symbols) => { return symbols.filter(symbol => symbol.startsWith(prefix)).map(symbol => ({ title: symbol || "?", - className: suggestStyle(style) + className: makeSuggestStyle(style) })); }; const currentNamespaceDescriptorPromise = dirac.extractNamespacesAsync().then(selectCurrentNamespace); - const resolvedNamespacePromise = currentNamespaceDescriptorPromise.then(currentNamespaceDescriptor => { + const resolvedNamespaceNamePromise = currentNamespaceDescriptorPromise.then(currentNamespaceDescriptor => { if (!currentNamespaceDescriptor) { return namespace; } @@ -447,16 +447,65 @@ WebInspector.DiracPromptWithHistory.prototype = { return allAliases[namespace] || namespace; // resolve alias or assume namespace name is a full namespace name }); - const namespaceSymbolsPromise = resolvedNamespacePromise.then(dirac.extractNamespaceSymbolsAsync).then(annotateQualifiedSymbols.bind(this, "suggest-cljs-qualified")); - const macroNamespaceSymbolsPromise = resolvedNamespacePromise.then(dirac.extractMacroNamespaceSymbolsAsync).then(annotateQualifiedSymbols.bind(this, "suggest-cljs-qualified suggest-cljs-macro")); + const prepareAnnotatedJavascriptCompletionsForPseudoNamespaceAsync = (namespaceName, callback) => { + return new Promise(resolve => { + const resultHandler = (expression, prefix, completions) => { + const annotatedCompletions = annotateQualifiedSymbols("suggest-cljs-qualified suggest-cljs-pseudo", completions); + if (dirac._DEBUG_COMPLETIONS) { + console.log("resultHandler got", expression, prefix, completions, annotatedCompletions); + } + resolve(annotatedCompletions); + }; - // order matters here, see _markAliasedCompletions below - const jobs = [ - namespaceSymbolsPromise, - macroNamespaceSymbolsPromise - ]; + this._loadJavascriptCompletions(requestId, namespaceName + ".", force, resultHandler); + }); + }; + + const readyCallback = completionsReadyCallback.bind(this, expression, prefix); + + const provideCompletionsForNamespace = ([namespaces, namespaceName]) => { + const namespace = namespaces[namespaceName]; + if (!namespace) { + const macroNamespaceNames = dirac.getMacroNamespaceNames(namespaces); + if (!macroNamespaceNames.includes(namespaceName)) { + if (dirac._DEBUG_COMPLETIONS) { + console.log("no known namespace for ", namespaceName); + } + readyCallback([]); + return; + } else { + if (dirac._DEBUG_COMPLETIONS) { + console.log("namespace is a macro namespace", namespaceName); + } + } + } + + if (namespace && namespace.pseudo) { + if (dirac._DEBUG_COMPLETIONS) { + console.log("pseudo namespace => falling back to JS completions", namespaceName); + } + prepareAnnotatedJavascriptCompletionsForPseudoNamespaceAsync(namespaceName).then(readyCallback); + return; + } + + if (dirac._DEBUG_COMPLETIONS) { + console.log("cljs namespace => retrieving symbols and macros from caches", namespaceName); + } + const namespaceSymbolsPromise = dirac.extractNamespaceSymbolsAsync(namespaceName) + .then(annotateQualifiedSymbols.bind(this, "suggest-cljs-qualified")); + const macroNamespaceSymbolsPromise = dirac.extractMacroNamespaceSymbolsAsync(namespaceName) + .then(annotateQualifiedSymbols.bind(this, "suggest-cljs-qualified suggest-cljs-macro")); + + // order matters here, see _markAliasedCompletions below + const jobs = [ + namespaceSymbolsPromise, + macroNamespaceSymbolsPromise + ]; + + Promise.all(jobs).then(concatAnnotatedResults).then(readyCallback); + }; - Promise.all(jobs).then(concatAnnotatedResults).then(completionsReadyCallback.bind(this, expression, prefix)); + Promise.all([dirac.extractNamespacesAsync(), resolvedNamespaceNamePromise]).then(provideCompletionsForNamespace); } else { // general completion (without slashes) // combine: locals (if paused in debugger), current ns symbols, namespace names and cljs.core symbols @@ -465,7 +514,7 @@ WebInspector.DiracPromptWithHistory.prototype = { const annotateSymbols = (style, symbols) => { return symbols.filter(symbol => symbol.startsWith(input)).map(symbol => ({ title: symbol || "?", - className: suggestStyle(style) + className: makeSuggestStyle(style) })); }; @@ -506,16 +555,33 @@ WebInspector.DiracPromptWithHistory.prototype = { const annotatedCompletions = filteredLocals.map(item => ({ title: item.name || "?", epilogue: item.identifier ? "js/" + item.identifier : undefined, - className: suggestStyle("suggest-cljs-scope") + className: makeSuggestStyle("suggest-cljs-scope") })); annotatedCompletions.reverse(); // we want to display inner scopes first return annotatedCompletions; }; - const annotateNamespaceNames = (style, namespaces) => { + const annotateNamespaceName = namespace => { + let extraStyle = ""; + if (namespace.pseudo) { + extraStyle += " suggest-cljs-pseudo"; + } + return { + title: namespace.name || "?", + className: makeSuggestStyle("suggest-cljs-ns" + extraStyle) + } + }; + + const annotateNamespaceNames = namespaces => { + return Object.keys(namespaces) + .filter(name => name.startsWith(input)) + .map(name => annotateNamespaceName(namespaces[name])); + }; + + const annotateMacroNamespaceNames = (namespaces) => { return namespaces.filter(name => name.startsWith(input)).map(name => ({ title: name || "?", - className: suggestStyle(style) + className: makeSuggestStyle("suggest-cljs-ns suggest-cljs-macro") })); }; @@ -523,43 +589,41 @@ WebInspector.DiracPromptWithHistory.prototype = { return Object.keys(namespaceDescriptors); }; - const extractMacroNamespaceNames = namespaceDescriptors => { - let names = []; - for (let descriptor of Object.values(namespaceDescriptors)) { - if (!descriptor.detectedMacroNamespaces) { - continue; - } - names = names.concat(descriptor.detectedMacroNamespaces); - } - return dirac.deduplicate(names); - }; - const annotateAliasesOrRefers = (kind, prefix, style, namespaceDescriptor) => { if (!namespaceDescriptor) { return []; } - const mapping = namespaceDescriptor[kind] || {}; - return Object.keys(mapping).filter(name => name.startsWith(input)).map(name => { - const targetName = mapping[name]; - return { - title: name, - epilogue: targetName ? prefix + targetName : null, // full target name - className: suggestStyle(style) - } + + return dirac.extractNamespacesAsync().then(namespaces => { + const mapping = namespaceDescriptor[kind] || {}; + return Object.keys(mapping).filter(name => name.startsWith(input)).map(name => { + const targetName = mapping[name]; + const targetNamespace = namespaces[targetName] || {}; + let extraStyle = ""; + if (targetNamespace.pseudo) { + extraStyle += " suggest-cljs-pseudo"; + } + return { + title: name, + epilogue: targetName ? prefix + targetName : null, // full target name + className: makeSuggestStyle(style + extraStyle) + } + }); + }); }; const annotateReplSpecials = symbols => { return symbols.filter(symbol => symbol.startsWith(input)).map(symbol => ({ title: symbol || "?", - className: suggestStyle("suggest-cljs-repl suggest-cljs-special") + className: makeSuggestStyle("suggest-cljs-repl suggest-cljs-special") })); }; 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(extractNamespaceNames).then(annotateNamespaceNames.bind(this, "suggest-cljs-ns")); - const macroNamespaceNamesPromise = dirac.extractNamespacesAsync().then(extractMacroNamespaceNames).then(annotateNamespaceNames.bind(this, "suggest-cljs-ns suggest-cljs-macro")); + 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 namespaceAliasesPromise = currentNamespaceDescriptor.then(annotateAliasesOrRefers.bind(this, "namespaceAliases", "is ", "suggest-ns-alias")); diff --git a/resources/unpacked/devtools/front_end/dirac/dirac.js b/resources/unpacked/devtools/front_end/dirac/dirac.js index 2746cec8d8..0521f4495d 100644 --- a/resources/unpacked/devtools/front_end/dirac/dirac.js +++ b/resources/unpacked/devtools/front_end/dirac/dirac.js @@ -213,6 +213,10 @@ Object.assign(window.dirac, (function() { return loadLazyDirac().then(() => window.dirac.invalidateNamespacesCache(...args)); } + function getMacroNamespaceNames(...args) { + return loadLazyDirac().then(() => window.dirac.getMacroNamespaceNames(...args)); + } + // --- exported interface --------------------------------------------------------------------------------------------------- // don't forget to update externs.js too @@ -254,7 +258,8 @@ Object.assign(window.dirac, (function() { extractNamespacesAsync: extractNamespacesAsync, invalidateNamespaceSymbolsCache: invalidateNamespaceSymbolsCache, invalidateMacroNamespaceSymbolsCache: invalidateMacroNamespaceSymbolsCache, - invalidateNamespacesCache: invalidateNamespacesCache + invalidateNamespacesCache: invalidateNamespacesCache, + getMacroNamespaceNames: getMacroNamespaceNames // ... diff --git a/resources/unpacked/devtools/front_end/dirac_lazy/dirac_lazy.js b/resources/unpacked/devtools/front_end/dirac_lazy/dirac_lazy.js index 025255119c..b13c5e9a65 100644 --- a/resources/unpacked/devtools/front_end/dirac_lazy/dirac_lazy.js +++ b/resources/unpacked/devtools/front_end/dirac_lazy/dirac_lazy.js @@ -133,16 +133,45 @@ Object.assign(window.dirac, (function() { /** * @param {string} url * @param {string} cljsSourceCode - * @return {?dirac.NamespaceDescriptor} + * @return {!Array} */ - function parseClojureScriptNamespace(url, cljsSourceCode) { + function parseClojureScriptNamespaces(url, cljsSourceCode) { + if (!cljsSourceCode) { + return []; + } var descriptor = dirac.parseNsFromSource(cljsSourceCode); if (!descriptor) { - return null; + return []; } descriptor.url = url; - return descriptor; + return [descriptor]; + } + + /** + * @param {string} url + * @param {string} jsSourceCode + * @return {!Array} + */ + function parsePseudoNamespaces(url, jsSourceCode) { + if (!jsSourceCode) { + return []; + } + + const result = []; + const re = /goog\.provide\('(.*?)'\);/gm; + let m; + while (m = re.exec(jsSourceCode)) { + const namespaceName = m[1]; + const descriptor = { + name: namespaceName, + url: url, + pseudo: true + }; + result.push(descriptor); + } + + return result; } /** @@ -151,43 +180,62 @@ Object.assign(window.dirac, (function() { * @suppressGlobalPropertiesCheck */ function parseNamespacesDescriptorsAsync(script) { - const sourceMap = WebInspector.debuggerWorkspaceBinding.sourceMapForScript(script); - if (!sourceMap) { + if (script.isInternalScript() || script.isContentScript()) { return Promise.resolve([]); } - var promises = []; - for (let 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 + let promises = []; + let realNamespace = false; + + const sourceMap = WebInspector.debuggerWorkspaceBinding.sourceMapForScript(script); + if (sourceMap) { + for (let 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, WebInspector.resourceTypes.SourceMapScript); + const namespaceDescriptorsPromise = contentProvider.requestContent().then(cljsSourceCode => parseClojureScriptNamespaces(url, cljsSourceCode)); + promises.push(namespaceDescriptorsPromise); + realNamespace = true; + } + } + } + + // we are also interested in pseudo namespaces from google closure library + if (!realNamespace) { + const url = script.contentURL(); const parser = document.createElement('a'); parser.href = url; - if (!parser.pathname.match(/\.clj.$/)) { - continue; + if (parser.pathname.match(/\.js$/)) { + const namespaceDescriptorsPromise = script.requestContent().then(jsSourceCode => parsePseudoNamespaces(url, jsSourceCode)); + promises.push(namespaceDescriptorsPromise); } - const contentProvider = sourceMap.sourceContentProvider(url, WebInspector.resourceTypes.SourceMapScript); - const namespaceDescriptorPromise = contentProvider.requestContent().then(cljsSourceCode => parseClojureScriptNamespace(url, cljsSourceCode || "")); - promises.push(namespaceDescriptorPromise); } - const removeNullDescriptors = - /** - * - * @param {!Array} namespaceDescriptors - * @return {!Array} - */ - namespaceDescriptors => { - return namespaceDescriptors.filter(descriptor => !!descriptor); - }; + const concatResults = results => { + return [].concat.apply([], results); + }; - // parseClojureScriptNamespace may fail and return null, so we filter out null results after gathering all results - return Promise.all(promises).then(removeNullDescriptors); + return Promise.all(promises).then(concatResults); } // --- namespace names -------------------------------------------------------------------------------------------------- + function getMacroNamespaceNames(namespaces) { + let names = []; + for (let 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([]); @@ -216,8 +264,7 @@ Object.assign(window.dirac, (function() { const uiSourceCodes = getRelevantSourceCodes(workspace); const promises = []; - for (var i = 0; i < uiSourceCodes.length; i++) { - const uiSourceCode = uiSourceCodes[i]; + for (let uiSourceCode of uiSourceCodes) { const namespaceDescriptorsPromise = getSourceCodeNamespaceDescriptorsAsync(uiSourceCode); promises.push(namespaceDescriptorsPromise); } @@ -586,7 +633,8 @@ Object.assign(window.dirac, (function() { extractMacroNamespaceSymbolsAsync: extractMacroNamespaceSymbolsAsync, invalidateMacroNamespaceSymbolsCache: invalidateMacroNamespaceSymbolsCache, extractNamespacesAsync: extractNamespacesAsync, - invalidateNamespacesCache: invalidateNamespacesCache + invalidateNamespacesCache: invalidateNamespacesCache, + getMacroNamespaceNames: getMacroNamespaceNames }; })()); diff --git a/resources/unpacked/devtools/front_end/externs.js b/resources/unpacked/devtools/front_end/externs.js index 7c3aa3bbca..7ef6cfbde0 100644 --- a/resources/unpacked/devtools/front_end/externs.js +++ b/resources/unpacked/devtools/front_end/externs.js @@ -425,6 +425,13 @@ var dirac = { invalidateMacroNamespaceSymbolsCache: function (namespaceName) {}, invalidateNamespacesCache: function() {}, + /** + * + * @param {Object.} namespaces + * @return {Array.} + */ + getMacroNamespaceNames: function(namespaces) {}, + /** * @param {Array.} coll * @param {function(T):string=} keyFn @@ -514,6 +521,7 @@ dirac.ScopeInfo; * @typedef {{ * name:!string, * url:!string, + * pseudo:?boolean, * namespaceAliases:?Object., * namespaceRefers:?Object., * macroNamespaceAliases:?Object., diff --git a/resources/unpacked/devtools/front_end/ui/suggestBox.css b/resources/unpacked/devtools/front_end/ui/suggestBox.css index f7164f1443..1f5a3ffb1a 100644 --- a/resources/unpacked/devtools/front_end/ui/suggestBox.css +++ b/resources/unpacked/devtools/front_end/ui/suggestBox.css @@ -133,31 +133,38 @@ position: relative; margin-right: 6px; color: #ccc; - top: -1px; text-align: right; } .suggest-box .suggest-box-content-item.suggest-cljs .prologue::before { - border-left: 2px solid #899fcb; - border-right: 2px solid #899fcb; + height: 6px; + width: 0px; + display: inline-block; + border-left: 3px solid #aed17d; + border-right: 3px solid #aed17d; border-radius: 1px; content: ""; -webkit-user-select: none; } .suggest-box .suggest-box-content-item.suggest-cljs-macro .prologue::before { - border-left: 2px solid #aed17d; - border-right: 2px solid #aed17d; + border-left-color: #d1585d; + border-right-color: #d1585d; +} + +.suggest-box .suggest-box-content-item.suggest-cljs-pseudo .prologue::before { + border-left-color: #899fcb; + border-right-color: #899fcb; } .suggest-box .suggest-box-content-item.suggest-cljs-special .prologue::before { - border-left: 2px solid #e6bf73; - border-right: 2px solid #e6bf73; + border-left-color: #e6bf73; + border-right-color: #e6bf73; } .suggest-box .suggest-box-content-item.suggest-cljs-combined-ns-macro .prologue::before { - border-left: 2px solid #899fcb; - border-right: 2px solid #aed17d; + border-left-color: #d1585d; + border-right-color: #aed17d; } .suggest-box .suggest-box-content-item.suggest-cljs-ns .prologue::after { @@ -202,11 +209,10 @@ .suggest-box .suggest-box-content-item .epilogue { font-size: 8px; - float: right; + display:inline-block; color: #ccc; - padding: 0 6px; - position: relative; - top: 2px; + padding: 0 4px; + text-align: right; } .suggest-box .suggest-box-content-item.selected {