blob: 31df4854d42c96f3210cd3f37e75d80960ce0c33 [file] [log] [blame]
;; Copyright 2014 Google Inc. All rights reserved.
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
(ns ui.xrefs
"View for XRefs pane"
(:require [cljs.core.async :refer [put!]]
[clojure.string :refer [trim]]
[goog.crypt.base64 :as b64]
[goog.string :as gstring]
[goog.string.format]
[om.core :as om :include-macros true]
[om.dom :as dom :include-macros true]
[ui.schema :as schema]
[ui.service :as service]
[ui.util :refer [fix-encoding ticket->vname]]))
(defn- page-navigation [xrefs-to-view pages current-page-token ticket]
(let [current-page (first (first (filter #(= (second %) current-page-token) (map-indexed (fn [i t] [i t]) pages))))
prev-page (when (> current-page 0)
(nth pages (dec current-page)))
next-page (when (< (inc current-page) (count pages))
(nth pages (inc current-page)))]
(dom/nav nil
(apply dom/ul #js {:className "pagination pagination-sm"}
(concat
[(dom/li #js {:className (when-not prev-page "disabled")}
(dom/a #js {:href "#"
:ariaLabel "Previous"
:onClick (when prev-page
(fn [e]
(.preventDefault e)
(put! xrefs-to-view {:ticket ticket
:page_token prev-page})))}
"<"))]
(map-indexed (fn [page token]
(dom/li #js {:className (when (= token current-page-token)
"active")}
(dom/a #js {:href "#"
:onClick (fn [e]
(.preventDefault e)
(put! xrefs-to-view {:ticket ticket
:page_token token}))}
(str (inc page)))))
pages)
[(dom/li #js {:className (when-not next-page "disabled")}
(dom/a #js {:href "#"
:ariaLabel "Next"
:onClick (when next-page
(fn [e]
(.preventDefault e)
(put! xrefs-to-view {:ticket ticket
:page_token next-page})))}
">"))])))))
(defn- collect-anchors [anchors]
(into {}
(for [[file anchors] (group-by :parent anchors)]
[file (sort-by (comp :byte_offset :start) anchors)])))
(defn- display-anchor [file anchor file-to-view]
(let [line (:line_number (:snippet_start anchor))
snippet (:snippet anchor)]
(when (and line snippet)
(dom/p #js {:className "snippet"}
(dom/a #js {:href "#"
:title (gstring/format "[%d:%d-%d:%d) %s %s"
line (:column_offset (:start anchor))
(:line_number (:end anchor)) (:column_offset (:end anchor))
(:kind anchor)
(:text anchor))
:onClick (fn [e]
(.preventDefault e)
(put! file-to-view
{:ticket file
:anchor (:ticket anchor)
:line line}))}
(str line))
;; TODO(schroederc): narrow/expand multi-line snippets
": " (dom/span nil (trim snippet))))))
(defn- display-anchors [title anchors file-to-view]
(when anchors
(dom/ul nil
(dom/li nil
(dom/strong nil title)
(apply dom/ul nil
(map (fn [[file anchors]]
(apply dom/li nil
(:path (ticket->vname file))
(filter identity (map (fn [anchor] (display-anchor file anchor file-to-view)) anchors))))
(collect-anchors anchors)))))))
(defn- xrefs-view [state owner]
(reify
om/IRenderState
(render-state [_ {:keys [file-to-view xrefs-to-view anchor-info]}]
(apply dom/div #js {:id "xrefs"
:className "row border"
:style (when (or (:hidden state) (empty? state))
#js {:display "none"})}
(dom/button #js {:id "close-xrefs"
:className "close btn btn-danger btn-xs"
:onClick #(om/transact! state :hidden (constantly true))}
(dom/span #js {:className "glyphicon glyphicon-remove"}))
(cond
(:hidden state) nil
(:failure state) [(dom/p nil "Sorry, an error occurred!")
(dom/p nil (:original-text (:parse-error state)))]
(:loading state) [(dom/span #js {:title (:loading state)}
"Loading xrefs... ")
(dom/span #js {:className "glyphicon glyphicon-repeat spinner"})]
:else
[(dom/strong nil (:ticket (:cross-references state)))
(display-anchors "Definitions:" (:definition (:cross-references state)) file-to-view)
(display-anchors "Documentation:" (:documentation (:cross-references state)) file-to-view)
(display-anchors "References:" (:reference (:cross-references state)) file-to-view)
(when (:related_node (:cross-references state))
(dom/ul nil
(dom/li nil
(dom/strong nil "Related Nodes:")
(apply dom/ul nil
(map (fn [[relation nodes]]
(dom/li nil
(dom/strong nil relation) " "
(apply dom/ul nil
(map (fn [{:keys [ticket]}]
(dom/li nil
(str
(if-let [kind (get (into {} (map (juxt :name :value) (:fact (get @(:nodes state) (keyword ticket)))))
schema/node-kind-fact)]
(fix-encoding (b64/decodeString kind))
"UNKNOWN") " ")
(dom/a #js {:href "#"
:onClick (fn [e]
(.preventDefault e)
(put! xrefs-to-view ticket))}
ticket)))
nodes))))
(group-by :relation_kind (:related_node (:cross-references state))))))))
(page-navigation xrefs-to-view (cons "" (:pages state)) (or (:current-page-token state) "") (:ticket (:cross-references state)))])))))