Giter Site home page Giter Site logo

Comments (35)

datwaft avatar datwaft commented on May 29, 2024 1

One snapshot of the code used for this, so you can learn a bit about the inner workings of the macros:

(macro print-all! [iter]
  `(do
     ,(unpack (icollect [_ item (ipairs iter)]
                `(print ,(tostring item))))))

This macro lets you convert this:

(print-all! [1 2 3 4])

Into this:

(do
  (print "1")
  (print "2")
  (print "3")
  (print "4"))

It's very useful to convert a list of something into a list of expressions.

This bit is the one that is used to set the mapping for all the modes specified by the user.

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024 1

@datwaft many thanks for quick response. Today and tmr I haven't got a chance to use the computer. I'll definitely give you more feedback this Friday or Saturday. My first impression is very positive with the syntax.

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024 1

Those functions are inside the macro because I wanted to make the macro self-contained to share it here, so you of anyone else could just copy it and test it. The final version will have those functions outside of the macro.

The use of pcall and creating new functions is because I don't want my macros to depend on any external package. Therefore, you can use them without needing to necessarily use Packer or Aniseed, you use Hotpot as an alternative, for example.

It's not much a problem, because it's very easy to reinvent the wheel in Lisp (see the Lisp curse) even if I don't really like to do it.

I learned a lot by working on that last macro, so yes, a rework is in order for all the other macros.

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024 1

It's just a right time when I'm about to try hotspot instead of aniseed 😂. I'll test the macro this evening and give you feedback later. Have good weekend my dear.

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024 1

Thanks for finding the bug!

With the command-mode mapping I wasn't referring to the which-key popup, the mapping just didn't do anything, not even writing <space>, and that happens with all the which-key mappings that use a function as rhs.

I will probably just use your fix for the non-which-key version in the which-key version.

Update 04

About this, it would be kinda difficult to implement because the mapping would need to be able to split the prefix from the rest of the macro, and how would you implement that? With <leader> it would be easy, but with other prefixes it would be kinda difficult (e.g. g). Maybe we could just ignore difficult prefixes like that.

When writing the macro I just thought that it would work even with no prefix.

This could be limitation of trying the make the macro work for which-key and normal mappings. If we just separated them we would be able to use another syntax more native to which-key, which would solve the problem.

Edit:
What happened in Update 1 shouldn't have happened, I the macro I gave you it doesn't do the processing of the function into a symbol, it just passes it directly to the which-key register command. That means that in that case the macro wasn't detecting which-key and used a native mapping.

You can wrap your macro in (macrodebug ...) to see what kind of result it gives.

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024 1

That would be a good first approach.

Learning Lisp is veeeery fun. :D

I will continue to work on this on my side. I had some ideas that could be worth implementing.

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024 1

@datwaft here is a few thought after short investigation from your macro

  1. exist? working!
  2. map! working with no relation to which-key module. So I'd prefer to keep map! syntax this way, the good thing is it has buffer integrated as well, no need for another buf-map! implementation
  3. We should only focus on which-key module. I found out later on you use ,lhs which will be translated to <Leader>0, this is not correct. We have to trim down <Leader> to make it work. Here is proof of concept which work on my side ("Nvim Conf" is shown and <Leader>0 open Telescope popup)
(if (exists? :which-key)
  (let [statements (icollect [_ mode (ipairs modes)]
                     `((. (require :which-key) :register) {(: ,lhs :sub 9 -1) [,rhs ,?name]}
                                                          {:prefix "<Leader>"}))]
      (if (> (length statements) 1)
          `(do
             ,(unpack statements))
          `,(unpack statements)))

Please note that this is only for 1 level of mapping, let's say we want 2 level of mapping such as <Leader>lf, we will not be able to use the above example. However, we can implement this with something like the following pseudo-code

- Trim `<Leader>` from `lhs`, get the remaining string returned value `ret`
- If `ret` only has 1 character, give it value of `[,rhs ,?name]`, else...
- Split `ret` into each character
- Recursively repeat or using `while` loop to create a _nested_ table with last char from `ret` will have value of `[,rhs ,?name]`

What do you think? Due to work and I'm not a developer by trade, my time with computer is very limited, I do wish I could help you more.

Update: My bad, I typed too quick, I found out I haven't commented out :0 "Nvim Conf" from which-key config, so the above conclusion is not correct sadly. Now I turn my double to exist? not sure if it invoke which-key properly. Will do more investigation when I got time.

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024 1

Edit: this could be out of topic, I think macros.fnl in general can be clear and concise if you make use of pattern matching and thread macros. What do you think?

What do you think about 7177711, I think the macros are now more clear and concise.

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024 1

What do you think about 7177711, I think the macros are now more clear and concise.

Well done brother. Look much better and elegant. Please add buf-map! and buf-noremap! macro as well, that would be great!

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024 1

Done! See cb51bc5.

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024 1

BTW, added your fix for autocmd! to master.

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024 1

BTW, added your fix for autocmd! to master.

Sure, I'll do this tonight.

Thanks for your suggestion, that makes sense to me 👍

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024 1

I got the idea of just using the documentation part of the register function to add the name of the mapping and using the standard way of defining mappings for the mapping.

Tomorrow I will try the implementation, should be easy to do!

What do you think about c296089.
It seems to work perfectly for me.

Here is the code:

map! macro implementation
(fn doc-map! [mode lhs options description]
  `(let [(ok?# which-key#) (pcall require :which-key)]
     (when ok?#
       (which-key#.register
         {,lhs ,description}
         {:mode ,mode
          :buffer ,(if options.buffer 0)
          :silent ,(if options.silent true)
          :noremap ,(if (not options.noremap) false)
          :nowait ,(if options.nowait true)}))))

(fn map! [[modes & options] lhs rhs ?description]
  "Defines a vim mapping
  Allows functions as right-hand side"
  (fn expr [buffer? mode lhs rhs options]
    (if buffer?
      `(vim.api.nvim_buf_set_keymap 0 ,mode ,lhs ,rhs ,options)
      `(vim.api.nvim_set_keymap ,mode ,lhs ,rhs ,options)))
  (let [modes (-> modes
                  (->str)
                  (str->seq))
        buffer? (contains? options :buffer)
        options (seq->set options)
        options (if (fn? rhs)
                  (conj options [:expr true])
                  options)
        fn-name (when (fn? rhs)
                  (gensym-fn!))
        exprs (map #(expr buffer? $ lhs
                          (if (fn? rhs)
                            (string.format "v:lua.%s()" fn-name)
                            rhs)
                          (disj options :buffer))
                   modes)
        exprs (if (fn? rhs)
                (cons `(global ,(sym fn-name) ,rhs) exprs)
                exprs)
        exprs (if (and ?description (exists? :which-key))
                (conj exprs (unpack (map #(doc-map! $ lhs options ?description)
                                         modes)))
                exprs)]
    (if (> (length exprs) 1)
      `(do ,(unpack exprs))
      (unpack exprs))))

It should be even more readable than the last one.

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024 1

Awesome @datwaft, thank you so much for taking your time to feature this macro. I had a quick test for a while ago and it worked! I also noted that you fixed the autocmd! so I think it's safe to close this issue. However, I'll let you know if I run into problem (hope not) with this new mapping. Cheers!

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024

Nice idea. I see both options to be possible.

I haven't used which-key.nvim yet, so I would need to investigate.

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024

I think we can introduce a new macro wk-map! n wk-noremap! only for those who use which-key.nvim

For reference, pls see it here

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024

I think we can introduce a new macro wk-map! n wk-noremap! only for those who use which-key.nvim

For reference, pls see it here

Interesting idea, I think that it would be ideal to add which-key support to the actual mapping instead of creating a new one, but only using the plugin if they opt-in by adding a description or the like (I still need to investigate, haven't had much time yet).

One problem of this idea would be that the options for the mapping will have to be moved, maybe doing it like zest does.

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024

Interesting idea, I think that it would be ideal to add which-key support to the actual mapping instead of creating a new one, but only using the plugin if they opt-in by adding a description or the like (I still need to investigate, haven't had much time yet).

Yes, I'd love to do so as we discussed in the beginning. I probably thought too much 😀

One problem of this idea would be that the options for the mapping will have to be moved, maybe doing it like zest does.

How about forcing which-key description as last option and then extract that one out, and keep the remaining as mapping options?

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024

I will create some demos and then we can decide which one is better. I will comment in this issue when I have the demos created.

I read the documentation of which-key and it's very interesting.

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024

Great, I'm glad that you take this into implementation. I'm happy to test it out once they're available ❤️❤️❤️

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024

Thank you, your example makes sense to me now. Love the way it look 😀

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024

I created this macro:

The macro declaration (it's long)
(macro map! [options lhs rhs ?name]
  (lambda exists? [module-name]
    "This function verifies if a module exists returning nil if it doesn't and
    returning the module if it does"
    (let [(ok?# content#) (pcall require module-name)]
      (if ok?# content# nil)))
  (lambda string? [obj]
    (= :string (type obj)))
  (lambda nil? [obj]
    (= :nil (type obj)))
  (lambda map [fun iter]
    (if (string? iter) (icollect [char (string.gmatch iter ".")] (fun char))
      (icollect [_ value (ipairs iter)] (fun value))))
  (lambda contains? [iter obj]
    (not= 0 (length (icollect [_ value (ipairs iter)]
                      (when (= obj value) value)))))
  (lambda function? [obj]
    (and
      (list? obj)
      (or
        (= (tostring (. obj 1)) :hashfn)
        (= (tostring (. obj 1)) :fn))))
  (global __core_symfn_id 0)
  (lambda gensym-fn! []
    "Generates a new unique variable name following the structure __core_symfn_#"
    (.. "__core_symfn_"
        (do
          (global __core_symfn_id (+ __core_symfn_id 1))
          __core_symfn_id)))
  (let [options (if (table? options) options [options])
        [modes & options] options
        modes (map #$ (tostring modes))
        options (map #(tostring $) options)
        lhs (tostring lhs)
        rhs (if (sym? rhs) (tostring rhs) rhs)]
    (if (exists? :which-key)
      (let [statements (icollect [_ mode (ipairs modes)]
                         `((. (require :which-key) :register)
                           {,lhs [,rhs ,?name]}
                           {:mode ,mode
                            :buffer ,(when (contains? options :buffer) 0)
                            :silent ,(when (contains? options :silent) true)
                            :noremap ,(if (not (contains? options :noremap)) false)
                            :nowait ,(when (contains? options :nowait) true)}))]
        (if (> (length statements) 1)
          `(do
             ,(unpack statements))
          `,(unpack statements)))
      (let [buffer? (contains? options :buffer)
            options (collect [_ option (ipairs options)]
                      (when (not= :buffer option)
                        (values (tostring option) true)))]
        (if (function? rhs)
          (let [fnsym (gensym-fn!)
                statements (icollect [_ mode (ipairs modes)]
                             (if buffer?
                               `(vim.api.nvim_buf_set_keymap 0 ,mode ,lhs ,(string.format "v:lua.%s()" fnsym) ,options)
                               `(vim.api.nvim_set_keymap ,mode ,lhs ,(string.format "v:lua.%s()" fnsym) ,options)))
                statements [`(global ,(sym fnsym) ,rhs) (unpack statements)]]
            `(do
               ,(unpack statements)))
          (let [statements (icollect [_ mode (ipairs modes)]
                             (if buffer?
                               `(vim.api.nvim_buf_set_keymap 0 ,mode ,lhs ,rhs ,options)
                               `(vim.api.nvim_set_keymap ,mode ,lhs ,rhs ,options)))]
            (if (> (length statements) 1)
              `(do
                 ,(unpack statements))
              `,(unpack statements))))))))

This macro lets you do this:

; --------------------------------------------------------------------------------
> (map! [n :silent] "<leader>f" "<cmd>lua print('hello world')<cr>"
                  "Print hello world")
((. (require "which-key") "register")
 {:<leader>f ["<cmd>lua print('hello world')<cr>" "Print hello world"]}
 {:mode "n" :noremap false :silent true})
; --------------------------------------------------------------------------------
> (map! [n :silent] "<leader>f" #(print "hello world")
                        "Print hello world")
((. (require "which-key") "register")
 {:<leader>f [(hashfn (print "hello world")) "Print hello world"]}
 {:mode "n" :noremap false :silent true})

And verifies that that only happens when which-key is available.

What do you think?

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024

@datwaft, felt bored at my workplace so I looked into your demo on mobile. I really like the new macros in general. I see you used some functions inside the macros such as nil?, string?... I think

  1. We could move it out of this macro scope for reusing in future
  2. Do we need reinvent the wheel? Because aniseed.core has these built-in. Or is this just for educational purposes?
  3. exist? used pcall to check module. What's different does it make from the recommendation at packer.nvim README
if packer_plugins["vim-fugitive"] and packer_plugins["vim-fugitive"].loaded then
print("Vim fugitive is loaded")
-- other custom logic
end

My opinion is we could refactor the macro, make it better 😘

Edit: this could be out of topic, I think macros.fnl in general can be clear and concise if you make use of pattern matching and thread macros. What do you think?

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024

I'm about to ask you to add buf-map! which is useful for some places such as lsp...

(lambda buf-map! [bufnr mode-list combination command ...]
  "Maps a combination to a command in some modes"
  (let [mode-list (tostring (if (sequence? mode-list) (. mode-list 1) mode-list))
        combination (tostring combination)
        options (collect [_ option (ipairs [...])]
                  (values (tostring option) true))
        out []]
    (if (and (list? command)
             (or (= (tostring (. command 1)) :hashfn)
                 (= (tostring (. command 1)) :fn)))
        (let [name (gensym-fn!)]
          (table.insert out `(global ,(sym name) ,command))
          (each [mode (string.gmatch mode-list ".")]
            (table.insert out
                          `(vim.api.nvim_buf_set_keymap ,(or bufnr 0) ,mode
                                                        ,combination
                                                        ,(string.format "v:lua.%s()"
                                                                        name)
                                                        ,options))))
        (each [mode (string.gmatch mode-list ".")]
          (table.insert out
                        `(vim.api.nvim_buf_set_keymap ,(or bufnr 0) ,mode
                                                      ,combination
                                                      ,(tostring command)
                                                      ,options))))
    (if (> (length out) 1)
        `(do
           ,(unpack out))
        `,(unpack out))))

to realized you have it in new macro :D

Update I had a quick test today, the keymap worked but which-key doesn't register the description specified in keymap. As shown in the screenshot, I disable <Leader>0 description in which-key config, then put that description in new wk-map! which is just a copy of your above snippet, as you can see in the which-key popup, 0 is shown the actual command, not Nvim Conf.

Screen Shot 2021-08-13 at 10 02 23 pm

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024

I am not really understanding what is the problem. Can you give me the code that doesn't work using the macro and the code that works but using native which-key commands?

By the way, are you using functions as the right-hand side for the mapping? I am currently testing the macro but can't get this simple mapping to work:

; Use <space> to close wildmenu
(noremap! [c] "<space>" #(if
                           (= (vim.fn.wildmenumode) 1) "<C-y>"
                           "<space>")
          "Close wildmenu with <space> if it's open")

It just doesn't do anything.

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024

@datwaft, FYI, I haven't tested with short-hand function yet. The other thing I noticed in your mapping is you use command mode which doesn't make sense to me, which-key pop-up should work in normal mode. In the screenshot above I already showed you everything, here is the code

(wk-map! [n :noremap :nowait] :<Leader>0
          "<Cmd>lua require'telescope.builtin'.find_files{cwd = os.getenv('HOME')..'/nix/dotfiles/nvim'}<CR>" "Nvim Conf"
          )

in which-key config file I have this commented out

(which-key.register {:1 "which_key_ignore"
                     :2 "which_key_ignore"
                     :3 "which_key_ignore"
                     :4 "which_key_ignore"
                     :5 "which_key_ignore"
                     :6 "which_key_ignore"
                     :7 "which_key_ignore"
                     :8 "which_key_ignore"
                     :9 "Dotfiles"
                     ;; This is not needed if `wk-map!` works!
                     ;; :0 "Nvim Conf"
                     :<Space> "Find All"
                     ";" "Live RipGrep"
                     ... }
                    {:prefix :<Leader>})

So <Leader>0 actually open Telescope but which-key pop-up window doesn't show Nvim Conf

Update 01: Yes, using #() produces an weird :'<,'>lua.__core_symfn_1() at the command line. This should be fixed.

Screen Shot 2021-08-14 at 4 38 21 pm

Update 02: The culprit could be???

(global __core_symfn_id 0)
(lambda gensym-fn! []
  "Generates a new unique variable name following the structure __core_symfn_#"
  (.. :__core_symfn_ (do
                       (global __core_symfn_id (+ __core_symfn_id 1))
                       __core_symfn_id)))

I tested with the original map! and noremap! and this also caused the same problem.

Screen Shot 2021-08-14 at 4 49 53 pm

Update 03: Found the bug, tracked down your macros.fnl and your snippet above, you didn't call the function, so the fix is

 `(vim.api.nvim_buf_set_keymap 0 ,mode ,lhs
                               ,(string.format ":<C-u>call v:lua.%s()<CR>"
                                               fnsym)
                               ,options)
 `(vim.api.nvim_set_keymap ,mode ,lhs
                           ,(string.format ":<C-u>call v:lua.%s()<CR>"
                                           fnsym)
                           ,options)))

Update 04: There seems wrong use which-key.register in your macro. If you look at README example, this function takes 2 tables {}, {prefix = <Leader>}, you're missing {:prefix :<Leader>} if I'm correct. Sorry that I can't be much of help now, that's all I can do today, baby is crying ;)

Here is my config for your reference https://github.com/babygau/nix/tree/tdt/dotfiles/nvim

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024

I do believe combine these 2 mappings is feasible but as you said, there are some obstacles needed to be resolved. I will try to implement this in my own free time (not sure how long, maybe weeks or months haha as I'm still struggling with macro and lisp in general speaking). But it's interesting and very worth to learn.

Edit: my first approach could be to implement which-key only macro, make sure it works as expected and then extend it's function to original mapping from there.

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024

Please add buf-map! and buf-noremap! macro as well, that would be great!

Currently (map! [mode :buffer] "lhs" "rhs") works like that, do you think it is really necessary?

If it is I can just copy the noremap! macro and make it add :buffer instead of :noremap.

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024

Please add buf-map! and buf-noremap! macro as well, that would be great!

Currently (map! [mode :buffer] "lhs" "rhs") works like that, do you think it is really necessary?

If it is I can just copy the noremap! macro and make it add :buffer instead of :noremap.

Yay, I absolutely think it's necessary cause it's easier to read. As you mentioned, I could do as you suggested but after all, Lisp is about how you express the data. However it depends on your decision, these are just my opinion 😘

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024

Nicely done @datwaft, got a clear view on my desktop now and it's clean, concise, and elegant. Have you got any idea for which-key?

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024

I got the idea of just using the documentation part of the register function to add the name of the mapping and using the standard way of defining mappings for the mapping.

Tomorrow I will try the implementation, should be easy to do!

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024

@datwaft I'm wondering should I make a pull request for a few more function such as command!, lua-command, buf-augroup!, buf-autocmd. Also found a bug in your autocmd, should remove call in (vim.cmd ,(string.format "autocmd %s %s call %s". Again, cheers on new macro refactors. You're doing really great! Apart from autocmd! bug, your macros are working on my side as well!

Edit: I'll leave my propose functions here, I think it's better this way, coz you might not need

(fn command! [name f]
  (vim.cmd (string.format "command! %s %s" (->str name) (->str f))))

(fn lua-command! [name f]
  (let [f (.. "lua " (->str f))]
    (command! (->str name) f)))
(fn augroup! [name ...]
  `(do
     (vim.cmd ,(string.format "augroup %s\nautocmd!"
                              (->str name)))
     (do
       ,...)
     (vim.cmd "augroup END")
     nil))

(fn buf-augroup! [name ...]
  `(do
     (vim.cmd ,(string.format "augroup %s\nautocmd! * <buffer>"
                              (->str name)))
     (do
       ,...)
     (vim.cmd "augroup END")
     nil))

;; Fixed version
(fn autocmd! [events pattern command]
  "Defines an autocommand"
  (let [events (if (sequence? events) events [events])
        events (-> (map ->str events) 
                   (table.concat ","))
        pattern (if (sequence? pattern) pattern [pattern])
        pattern (-> (map ->str pattern)
                    (table.concat ","))]
    (if (fn? command)
      (let [fn-name (gensym-fn!)]
        `(do
           (global ,(sym fn-name) ,command)
           (vim.cmd ,(string.format "autocmd %s %s call v:lua.%s()"
                                    events pattern fn-name))))
      `(vim.cmd ,(string.format "autocmd %s %s %s"
                                events pattern command)))))

(fn buf-autocmd! [events command]
  "Defines an autocommand"
  (let [events (if (sequence? events) events [events])
        events (-> (map ->str events) 
                   (table.concat ","))]
    (if (fn? command)
      (let [fn-name (gensym-fn!)]
        `(do
           (global ,(sym fn-name) ,command)
           (vim.cmd ,(string.format "autocmd %s <buffer> call v:lua.%s()"
                                    events fn-name))))
      `(vim.cmd ,(string.format "autocmd %s <buffer> %s"
                                events command)))))

Small question, is there a reason why you change from lambda to just fn?

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024

Sure, open the pull request, there I will give you some small adjustments.

And I did it because sometimes some parameters for the macro were evaluated to nil, and that crashed the macro, so I preferred to just use fn.

from nvim.conf.

bangedorrunt avatar bangedorrunt commented on May 29, 2024

Sure, open the pull request, there I will give you some small adjustments.

Thank you, but if you could wait, I will do it this weekend, later today, I found out my lua-command! is not working properly, somehow this was only loaded once, the second time onwards I opened nvim, all commands created from that macro were unavailable. If I don't use these command!, lua-command! macros, but use normal functions command!, lua-command! (I meant just define them as normal function and not place them in macros.fnl), they're working as expected. It's weird for me. Does it has anything to do with compile time vs runtime?

from nvim.conf.

datwaft avatar datwaft commented on May 29, 2024

Ah! Yes, I know what your problem is.
Macros that have side-effects should create the structure that does the side-effect. If not, it will only be done once at compile time.

Try with:

(fn command! [name f]
  `(vim.cmd ,(string.format "command! %s %s" (->str name) (->str f))))

from nvim.conf.

Related Issues (10)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.