vscode

Quiet in here? Check #calva-dev out :smiley:
2020-02-15T13:27:08.134700Z

@pez i think i have something that might be worth trying out now -- have you used tree sitter at all? if not, i can write up some notes on how to use things. if you're already familiar with tree sitter stuff, you can probably just replace the grammar.js file in one or the other of the tree-sitter-clojure repositories with this: https://gist.github.com/sogaiu/a6c7a6dca4259480ca960c4f8a594019 (i used tree-sitter-cli 0.16.4)

pez 2020-02-15T13:40:26.136400Z

No, never used tree sitter. And I'm not sure where I would plug it in, even if I think I have an idea about how it could fit in.

2020-02-15T13:42:17.139400Z

the tree-sitter cli provides a parse command which can measure execution time -- so one can at least get some sense by running it on individual clojure files.

pez 2020-02-15T13:42:21.139500Z

My processing of core.clj takes 150 ms, btw. Measured with checking the clock before and after. I don't know how much of that is the parsing. There is quite a lot going on in that proceess.

2020-02-15T13:43:07.140300Z

ah thanks for the numbers. borkdude and i did some measurements with clj-kondo's parsing -- we both got about what you got.

2020-02-15T13:43:39.140700Z

ofc we are all using different machines though 🙂

2020-02-15T13:44:12.141400Z

if you're interested, i'll write up some instructions. i've also made a few sample vscode extensions that use the grammar.

pez 2020-02-15T13:47:26.141800Z

I have a pretty speedy machine, even if it is a laptop.

2020-02-15T13:50:04.142300Z

i've been testing on a notebook as well.

pez 2020-02-15T13:51:38.144100Z

I'd like some instructions, yes. Not sure when I will have time to act on it, but I'd like to do some experimentation to try figure out a bit more about what kind of work would be involved.

2020-02-15T13:52:03.144900Z

ok, i'll work on putting a repository together with instructions then.

pez 2020-02-15T13:52:25.145400Z

If you want to do the measurement yourself, here's where I put my time stamps: https://github.com/BetterThanTomorrow/calva/blob/dev/src/cursor-doc/model.ts#L388

pez 2020-02-15T13:52:39.146100Z

At entry of that function and at exit.

2020-02-15T13:53:01.146600Z

ty! i hope to take a look soon -- gotta sleep first though 🙂

pez 2020-02-15T13:53:18.146800Z

Crazy TZ diffs! 😃

2020-02-15T13:53:36.147200Z

he he -- we can leverage the differences to get more work done sooner 😉

pez 2020-02-15T13:54:11.147600Z

vscode Clojure has someone at the watch 24/7.

2020-02-15T13:54:38.147800Z

🙂

pez 2020-02-15T13:57:13.148900Z

I'll try to time the rainbow paren parser as well. Even if that one is in no way a full parser.

pez 2020-02-15T13:59:21.151200Z

I imagine tree sitter would be the thing returning my tokens here: https://github.com/BetterThanTomorrow/calva/blob/dev/src/cursor-doc/model.ts#L41

2020-02-15T14:00:47.152300Z

you can essentially get back a tree of nodes (https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web/tree-sitter-web.d.ts#L50)

2020-02-15T14:02:17.153900Z

one way to use the results is to use tree sitter's tree traversal api. another way to use the tree is to construct a query to it and then get back matching info.

2020-02-15T14:02:56.154800Z

i also fed the root node to:

(defn node-seq
  [node]
  (tree-seq
    #(< 0 (.-childCount ^js %)) ; branch?
    #(.-children ^js %) ; children
    node)) ; root
to get back a sequence of nodes to process.

2020-02-15T14:04:32.155700Z

it's a bit ugly, but here are two ways of doing the same thing:

(defn doc-syms
  [^js doc ^js tok]
  (if (= "clojure" (.-languageId doc))
    (if-let [uri (.-uri doc)]
      (if-let [^js tree (get @trees uri)]
        (let [id-nodes (find-id-nodes (node-seq (.-rootNode tree)))]
          (->> id-nodes
            (map (fn [id-node]
                   (make-fn-sym-info (.-text id-node)
                     doc
                     (make-range id-node))))
            clj->js))
        #js [])
      #js [])
    #js [])) ; XXX: how to reduce this repetition

2020-02-15T14:04:45.156Z

;; uses tree sitter query api
(defn doc-syms-2
  [^js doc ^js tok]
  (if (= "clojure" (.-languageId doc))
    (if-let [uri (.-uri doc)]
      (if-let [^js tree (get @trees uri)]
        (let [q (.query @clj-lang
                  (str "((list "
                       "   (symbol) @head "
                       "   (symbol) @name) "
                       " (match? @head \"^def[a-zA-Z]*\"))"))
              ms (.matches q (.-rootNode tree))]
          (->> ms
            (keep (fn [match]
                    (when match
                      (when-let [caps (go/get match "captures")]
                        (when (= 2 (count caps))
                          (when-let [name-node (go/get (nth caps 1) "node")]
                            (make-fn-sym-info (.-text name-node)
                              doc
                              (make-range name-node))))))))
            clj->js))
        #js [])
      #js [])
    #js [])) ; XXX: how to reduce this repetition

2020-02-15T14:05:44.156900Z

those basically implemented provideDocumentSymbols

pez 2020-02-15T14:06:11.157200Z

I think it would be nice to build Calva's token cursor from a parser that more projects cared about.

pez 2020-02-15T14:09:23.159400Z

But I'm pretty fond of the token cursor as such, because it is very natural and intuitive to work with.

2020-02-15T14:09:56.160200Z

i'll have to study it to appreciate those observations 🙂

pez 2020-02-15T14:10:15.160600Z

I might whip up a symbol provider using the token cursor and we can compare, for the fun of it. 😃

2020-02-15T14:10:51.161200Z

sounds good to me -- hope that fits in your schedule 🙂

pez 2020-02-15T14:11:44.161800Z

Haha, well, not that I really have a schedule, but yeah, lots of things to do, no doubt.

2020-02-15T14:12:24.162200Z

i'm off for the evening -- good luck with your endeavors 👋

🤘 1
pez 2020-02-15T16:38:33.163600Z

So this is how symbols would be collected using Calva's LispTokenCursor:

function docSyms(document: vscode.TextDocument): vscode.SymbolInformation[] {
    const cursor: LispTokenCursor = docMirror.getDocument(document).getTokenCursor(0);
    let symbols: vscode.SymbolInformation[] = [];
    do {
        cursor.forwardWhitespace();
        cursor.downList();
        cursor.forwardWhitespace();
        const token: Token = cursor.getToken();
        if (token.type === 'id' && token.raw.startsWith('def')) {
            while(cursor.forwardSexp()) {
                cursor.forwardWhitespace();
                if (token.type === 'id') {
                    symbols.push(makeSymbol(document, cursor));
                    break;
                }
            }
        }
        cursor.forwardList();
        cursor.upList();
    } while (!cursor.atEnd());
    return symbols;
}

pez 2020-02-15T16:43:15.166400Z

I would implement symbols using nrepl, probably, but anyway, if a static method would be called for, the token cursor is very neat, imo.