Git Product home page Git Product logo

ctags-patterns-for-javascript's Introduction

Exuberant Ctags Patterns for JavaScript

The purpose of this project is to modernize and augment the many custom JavaScript Patterns for Exuberant Ctags that have been floating the web for years.

We want to make sure Exuberant Ctags doesn't miss a single named symbol in our whole code base and do so without unnecessary duplication:

This is done by disabling the default "kinds", creating new ones, and crafting as many patterns as necessary.

Reminders

Compatibility

These patterns are only usable with Exuberant Ctags. Universal Ctags is not currently supported and there's no plan to support it in the foreseeable future.

A note about disabling the default "kinds".

The option --javascript-kinds=-c-f-m-p-v in ctagsrc will disable the default kinds, c (classes), f (functions), m (methods), p (properties), and v (global variables), for JavaScript files.

This means your ctags program's builtin regex patterns or any user defined patterns registered against these kinds will no longer function and the patterns defined in ctagsrc will be the only patterns active for the JavaScript language.

This is done to have a single source of patterns and avoid duplicated tags.

Try

  1. Clone this repository:

     $ git clone https://github.com/romainl/ctags-patterns-for-javascript.git
    
  2. Build a tags file against the provided index.js:

     $ make tags
    

    or:

     $ ctags --options=NONE --options=./ctagsrc -f tags index.js
    

Use

  1. Clone this repository:

     $ git clone https://github.com/romainl/ctags-patterns-for-javascript.git
    
  2. In your shell, run the following command to tell Exuberant Ctags to use the options defined in the provided ctagsrc file:

     $ echo "--options=/absolute/path/to/ctags-patterns-for-javascript/ctagsrc" >> ~/.ctags
    

    with /absolute/path/to/ctags-patterns-for-javascript/ctagsrc being your actual path, of course.

  3. Use this command to generate a tags file at the root of your JavaScript project:

     $ ctags -R .
    

If for some reason the above instructions sound like Klingon to you, just copy the content of this file and paste it into your own ~/.ctags file. If that file doesn't exist, create it.

Tags

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
// FIXME: fix non-working Patterns                  | FIXME               | T
// TODO: better ES6+ support                        | TODO                | T
// BUG: there's really something fishy about this   | BUG                 | T
// ???: what the flying fuck?                       | ???                 | T
// !!!: dear god!                                   | !!!                 | T
// HACK: deployment is in 15 minutes                | HACK                | T
// XXX: I. Must. Finish. That. Mess. Quickly.       | XXX                 | T

Array literals

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
var|let|const array_name = [...                     | array_name          | A

Object literals

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
var|let|const object_name = {...                    | object_name         | O

Object properties

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
foo: 123,                                           | foo                 | P
foo: 123                                            | foo                 | P
bar: "eee",                                         | bar                 | P
bar: "eee"                                          | bar                 | P
baz: /regexp/,                                      | baz                 | P
baz: /regexp/                                       | baz                 | P
quux: []                                            | quux                | P
flax: {}                                            | flax                | P
var obj = { sluf: 1 };                              | sluf                | P
this.foo = {}                                       | foo                 | P

Generator functions

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
function* generator_name() {...                     | generator_name      | G
function *generator_name() {...                     | generator_name      | G
function * generator_name() {...                    | generator_name      | G
function*generator_name() {...                      | generator_name      | G
const generator_name = function* () {...            | generator_name      | G
const generator_name = function * () {...           | generator_name      | G
* generator_name() {...                             | generator_name      | G

Free-form functions

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
function func_name() {...                           | func_name           | F
(function func_name() {...                          | func_name           | F
var|let|const func_name = function() {...           | func_name           | F
var|let|const function_name = (arg) => ...          | func_name           | F
var|let|const function_name = arg => ...            | func_name           | F
var|let|const function_name = (\n...) => ...        | func_name           | F
async function function_name() {...                 | func_name           | F
(async function function_name() {...                | func_name           | F
var|let|const function_name = async function() {... | func_name           | F
var|let|const function_name = async () => {...      | func_name           | F
var|let|const function_name = async arg => {...     | func_name           | F

Constructors and classes

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
class ClassName {...                                | ClassName           | C

Methods

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
this.method_name = function() {...                  | method_name         | M
method_name : function() {...                       | method_name         | M
method_name: (param) => ...                         | method_name         | M
method_name: param => ...                           | method_name         | M
static method_name() {...                           | method_name         | M
method_name() {...                                  | method_name         | M
method_name = (param) => ...                        | method_name         | M
method_name = param => ...                          | method_name         | M

Variables

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
var|let|const var_name = 1;                         | var_name            | V
var|let|const var_name = /regexp/;                  | var_name            | V
var|let|const var_name = 'foo';                     | var_name            | V
var|let|const var_name = "bar";                     | var_name            | V
var|let|const var_name = new ClassName();           | var_name            | V
var|let|const var_name;                             | var_name            | V
var|let|const ..., var_name;                        | var_name            | V
var|let|const ..., ..., var_name;                   | var_name            | V
var|let|const [ var1, var2, var3 ] = useState([]);  | var1, var2, var3    | V
var|let|const { var1, var2, var3 } = props;         | var1, var2, var3    | V
    var_name,                                       | var_name            | V
    var_name                                        | var_name            | V

Named imports

Tagging direct imports would be redundant so we only tag named imports.

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
import { * as imp1 }                                | imp1                | I
import { foo as imp2 }                              | imp2                | I
import { imp3, * as imp4 }                          | imp4                | I
import { imp5, bar as imp6 }                        | imp6                | I
import { imp7, imp8, * as imp9 }                    | imp9                | I
import { imp10, imp11, baz as imp12 }               | imp12               | I
import { * as imp13, * as imp14, * as imp15 }       | imp13, imp14, imp15 | I
import { foo as imp16, bar as imp17, baz as imp18 } | imp16, imp17, imp18 | I
import * as imp19                                   | imp19               | I
import foo as imp20                                 | imp20               | I
import imp21, * as imp22                            | imp22               | I
import imp23, bar as imp24                          | imp24               | I
import imp25, imp26, * as imp27                     | imp27               | I
import imp28, imp29, baz as imp30                   | imp30               | I
import * as imp31, * as imp32, * as imp33           | imp31, imp32, imp33 | I
import foo as imp34, bar as imp35, baz as imp36     | imp34, imp35, imp36 | I
foo as imp37                                        | imp37               | I

Flow imports

Same story as imports, tagging direct imports would be redundant so we only tag named imports.

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
import type {foo as impFlow01} from ...             | impFlow01           | I
import type * as impFlow02 from ...                 | impFlow02           | I
import typeof {foo as impFlow03} from ...           | impFlow03           | I
import typeof * as impFlow04 from ...               | impFlow04           | I

Named exports

Same story as imports, tagging direct exports would be redundant so we only tag named exports.

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
export { var1 as exp04, var2 as exp05 };            | exp04, exp05        | E
export let exp06, exp07;                            | exp06, exp07        | E
export var exp08, exp09, exp09b;                    | exp08, exp09        | E
export let exp10 = 1, exp11 = 2;                    | exp10, exp11        | E
export const exp12 = 1, exp13 = 2;                  | exp12, exp13        | E
export var exp14 = 1, exp15 = 2;                    | exp14, exp15        | E
export function exp16() {}                          | exp16               | E
export default { var1 as exp20, var2 as exp21};     | exp20; exp21        | E
export default let exp22, exp23;                    | exp22, exp23        | E
export default var exp24, exp25;                    | exp24, exp25        | E
export default let exp26 = 1, exp27 = 2;            | exp26, exp27        | E
export default const exp28 = 1, exp29 = 2;          | exp28, exp29        | E
export default var exp30 = 1, exp31 = 2;            | exp30, exp31        | E
export default function exp32() {}                  | exp32               | E

Styled components

"Visual primitives for the component age.", as they say on the site, are not exactly native types but they are certainly named and very likely to be reused so they deserve a spot, here.

CODE                                                | TAG                 | KIND
----------------------------------------------------|---------------------|-----
var|let|const Component = styled...                 | Component           | S
var|let|const Component = createGlobalStyle...      | Component           | S

Hack

Regular expressions dialect

Because Exuberant Ctags takes no responsibility about the regular expression engine it uses under the hood, we must use the dumbest dialect, BRE, for portability. This has a number of consequences:

  • no alternation,
  • no lookbehind,
  • no backreference,
  • a verbose and frustrating syntax.

But we are hackers, so we see challenges where others see obstacles, right?

Watch and re-index

The bundled Makefile has a very simple and very cheap watch phony target that will run make tags every second. In turn, the tags target will run ctags --options=./ctagsrc -f tags index.js only if ctagsrc or index.js have changed since last run.

This allows us to start the watcher in a terminal:

$ make watch

open ctagsrc, index.js, and the tags file in Vim in another terminal:

$ vim -O tags ctagsrc index.js +'set autoread' +'autocmd! CursorHold,CursorHoldI * checktime'

and watch our tags file change as we edit existing patterns in ctagsrc or add expressions in index.js.

ctags-patterns-for-javascript's People

Contributors

kindrowboat avatar michael-coleman avatar romainl avatar sukima avatar the-code-robot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ctags-patterns-for-javascript's Issues

Object functions are not recognized

Example:

testobj = {
  func1: () => {
    console.log('test1');
  },
  func2: () => {
    console.log('test2');
  }
};

In this case func1 is recognized but func2 is not.
Interesting that if the call inside func1 is removed then both func1 and func2 are recognized:

testobj = {
  func1: () => {
  },
  func2: () => {
    console.log('test2');
  }
};

Not finding components defined using styled-components

Hi, first of all, thanks for making such a comprehensive set of ctags rules. Much appreciated.

I have some components in my library defined like so:

import { Box, styled } from '@smooth-ui/core-sc'
import Theme from 'theme'

const FramedBox = styled(Box)`
  border: 1px solid ${Theme.black800};
  border-radius: 0px 5pt 5pt 5pt;
  padding: 0px 5% 0px 5%;
  display: grid;
  grid-template-columns: 45% 45%;
  grid-gap: 5%;
  overflow-y: auto;

  @media(min-width: 769px){
    height: 50vh;
  }

  @media (max-width: 992px){
    grid-template-columns: 100%;
  }
`

export default FramedBox

But when I try and Ctrl + ] on a consuming file, it says that E426: tag not found: FramedBox

Is there some additional customization I need to make this work?

module.exports is not matched

The following function is not matched:

module.exports = function sum (a, b) {
  return a + b
}

I've done a rule to match that case as a function, but don't know if you didn't want to support on purpose since this is also the case for export default function name1(…) { … } as you pointed out on issue #7.
Wouldn't make sense to create an default export tag type and put them together on that new tag type? What do you think?
(We could try to create a rule only for default named exports with body definition once the ones that doesn't have definition would be already taken by another rules)

Export Class Support

I'm pretty unfamiliar with the syntax for ctags regex and was wondering if there's a reason that the following wouldn't work?

export class Foo {
  // ...
}

I'd expect to be able to reach Foo as a class if that makes sense.

Over-restrictive regex for function definitions?

In the regex for function definitions (specifically looked at arrow functions but could also apply to others, didn't look):

--regex-javascript=/^[ \t]*var[ \t]\{1,\}\([a-z_$][A-Za-z0-9_$]\{1,\}\)[ \t]*=.\{1,\}=>/\1/F,Function,Functions/b
--regex-javascript=/^[ \t]*let[ \t]\{1,\}\([a-z_$][A-Za-z0-9_$]\{1,\}\)[ \t]*=.\{1,\}=>/\1/F,Function,Functions/b
--regex-javascript=/^[ \t]*const[ \t]\{1,\}\([a-z_$][A-Za-z0-9_$]\{1,\}\)[ \t]*=.\{1,\}=>/\1/F,Function,Functions/b

Why does the first character of the function need to be lower cased? I specifically have a React component definition:

const MyModal = ({ prop1, prop2 }) => {

}

export default MyModal

This definition is skipped because of the capital case component function name.

Flow support

I found this issue on the Flow repo and I was wondering if the ctags-patterns-for-javascript project might be a good way to solve the problem?

facebook/flow#5322

Could not generate tags

I couldn't generate tags. Here's the error:

ctags: Warning: Don't reuse the kind letter `C' in a language JavaScript (old: "constant", new: "Class")
ctags: Warning: Don't reuse the kind letter `C' in a language JavaScript (old: "constant", new: "Class")
ctags: Warning: Don't reuse the kind letter `E' in a language JavaScript (old: "Export", new: "export")
ctags: Kind letter 'F' used in regex definition "^[     ]*function[     ]*([A-Za-z0-9_$]+)[     \(]" of JavaScript language is reserved in ctags main
make: *** [tags] Error 1

My ctags version

Universal Ctags 0.0.0(f480eed), Copyright (C) 2015 Universal Ctags Team
Universal Ctags is derived from Exuberant Ctags.
Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert
  Compiled: Jun  9 2017, 10:46:28
  URL: https://ctags.io/
  Optional compiled features: +wildcards, +regex, +multibyte, +option-directory, +coproc, +xpath, +case-insensitive-filenames

Default exports

Is there a reason that default exports aren't supported by this?

async / await support

Doesn't look like there's support for async/await style functions. For instance, This isn't getting picked up:

const myFn = async function({
 val1: mappingVal,
 val2,
 val3
}) {
  // ...
})

Not work for flow

The following code using flow doesn't match.

function test(): Boolean {
...
}

mjs files not working

Hi,

How to use your script with mjs files?
Working well with js files but not mjs files.

not picking up method signatures that span multiple lines

This works:

  extractEmailsFromString(text, options = { isWrappedLessThanGreaterThan: false, }) {
    if (options.isWrappedLessThanGreaterThan) {
      return text.match(/(<[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+>)/gi);
    }
    return text.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/gi);
  }

But this doesn't work:

  extractEmailsFromString(text, options = {
    isWrappedLessThanGreaterThan: false,
  }) {
    if (options.isWrappedLessThanGreaterThan) {
      return text.match(/(<[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+>)/gi);
    }
    return text.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/gi);
  }

I know ctags works on a line by line basis, so is this just not possible to implement? Thanks for making this project!

Folding of implicit functions

I've noticed that implicit return functions are not being folded. For example:

someFunc = () => 
  foo !== 'bar' ||
  baz < 42;

Not recognizing everything

Hi, I followed the instructions under the "Try" section of the readme.

After running make tags, the generated tags file only had this

!_TAG_FILE_FORMAT   2   /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED   1   /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_PROGRAM_AUTHOR    Darren Hiebert  /[email protected]/
!_TAG_PROGRAM_NAME  Exuberant Ctags //
!_TAG_PROGRAM_URL   http://ctags.sourceforge.net    /official site/
!_TAG_PROGRAM_VERSION   5.8 //
Comp01  index.js    /^var Comp01 = styled(Box)`$/;" v
Constr_var  index.js    /^var Constr_var = function() {$/;" c
Constr_var.constr_var_meth  index.js    /^  this.constr_var_meth = function() {$/;" m
OUT Makefile    /^OUT = @echo `date +[\\ %F\\ -\\ %T\\ ]`$/;"   m
SHELL   Makefile    /^SHELL := \/bin\/bash$/;"  m
class_constr_meth   index.js    /^      this.class_constr_meth = function(){$/;"    f
class_meth_equal    index.js    /^  class_meth_equal = function($/;"    f
const   index.js    /^const Constr_const = function() {$/;" c
const   index.js    /^const function_const = function() {$/;"   f
const.body  test.js /^    const config = { method, body: JSON.stringify(body) }$/;" p
const.constr_const_meth index.js    /^  this.constr_const_meth = function() {$/;"   m
function_basic  index.js    /^function function_basic() {$/;"   f
let index.js    /^let Constr_let = function() {$/;" c
let.constr_let_meth index.js    /^  this.constr_let_meth = function() {$/;" m
obj_lit.obj_lit_prop_array  index.js    /^  obj_lit_prop_array: [$/;"   p
obj_lit.obj_lit_prop_number index.js    /^  obj_lit_prop_number: 123,$/;"   p
obj_lit.obj_lit_prop_string index.js    /^  obj_lit_prop_string : "eee",$/;"    p
obj_lit_inline.obj_lit_inline_prop  index.js    /^var obj_lit_inline = { obj_lit_inline_prop: 1 };$/;"  p
this_prop.this_inner_prop   index.js    /^  this_inner_prop: "bar"$/;"  p
this_prop_method    index.js    /^this.this_prop_method = function($/;" f
var_declaration index.js    /^var var_declaration;$/;"  v
var_declaration_inlined_1_of_2  index.js    /^var var_declaration_inlined_1_of_2, var_declaration_inlined_2_of_2;$/;"   v
var_declaration_inlined_1_of_2_trailing_space   index.js    /^var var_declaration_inlined_1_of_2_trailing_space , var_declaration_inlined_2_of_2_trailing_space ;$/;"   v
var_declaration_inlined_1_of_3  index.js    /^var var_declaration_inlined_1_of_3, var_declaration_inlined_2_of_3, var_declaration_inlined_3_of_3;$/;"   v
var_declaration_inlined_1_of_3_trailing_space   index.js    /^var var_declaration_inlined_1_of_3_trailing_space , var_declaration_inlined_2_of_3_trailing_space , var_declaratio
n_inlined_3_of_3_trailing_space ;$/;"   v
var_declaration_trailing_space  index.js    /^var var_declaration_trailing_space ;$/;"  v
var_var_regexp  index.js    /^var var_var_regexp = \/regexp\/;$/;"  v

Here is my ctags --version

Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert
  Compiled: Sep 17 2017, 20:48:43
  Addresses: <[email protected]>, http://ctags.sourceforge.net
  Optional compiled features: +wildcards, +regex

How do I go about debugging why it isn't recognizing all the patterns?

ES6 class method support was lost

I'm not sure exactly when, but somewhere in the last several months the following regex pattern disappeared from the file that is now 'ctagsrc':

--regex-javascript=/^[ \t]*\([A-Za-z0-9_$]\{1,\}\)(.*)[ \t]*[{]/\1/M,Method,Methods/b

It is still listed in the readme though.

Without it, support for the following patterns is lost:

method_name() {
method_name(params) {

Variable names starting with `_`

Hi,

Thanks for your work on this.
So some of my variables/consts/lets/functions that start with an _ are not recognized.

Screenshot 2020-03-18 at 11 06 53

How do I get ctags to recognize these particular variables? I'd appreciate some pointers to achieve this.

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.