I've been trying in vain the past few days to get Vim to recognize tags in my JS file. My problem is very similar to this other question: How to view javascript tags in vim using tagbar + jsctags + tern_for_vim + ctags_exuberant? and I have a similar setup:

  1. Universal ctags installed (since exuberant-ctags seems to be not updating as much)
  2. Jsctags via a global gem
  3. tern_for_vim

I've tried to debug the tagbar plugin and it seems to call jsctags fine and does not print any errors. I've also tried to run jsctags via the command line with this command per the git repo's readme:

find . -type f -iregex ".*\.js$" -not -path "./node_modules/*" -exec jsctags {} -f \; | sed '/^$/d' | sort > tags

This generates a tags file fine, and if I run echo tagfiles() the output gives me ['tags'], which means it's pointing to the correct tag file location (confirmed using :pwd). :tag gives me tag stack empty and if I put my cursor over any method or object and hit Ctrl-] I'll get tag not found: Controller.

I've referenced Can't get Ctags working and How to view javascript tags in vim using tagbar + jsctags + tern_for_vim + ctags_exuberant?, both of which don't have solutions, hence the question here.

Attaching a section of my .vimrc to show my list of plugins, js file and .ctags if relevant:


Plugin 'gmarik/Vundle.vim' " Plugin Management
Plugin 'tmhedberg/matchit' " Matching configuration for %
Plugin 'jelera/vim-javascript-syntax' " Additional JS syntax highlighting
Plugin 'tpope/vim-surround' "provides mappings to easily delete, change and add surroundings like quotes, parenths etc
Plugin 'plasticboy/vim-markdown' "Markdown support
Plugin 'davidoc/taskpaper.vim' "Taskpaper support
Plugin 'tpope/vim-commentary' " Simple commenting in vim 
Plugin 'scrooloose/nerdtree' "Tree navigation
Plugin 'Xuyuanp/nerdtree-git-plugin' "Nerdtree git tags for files support
Plugin 'scrooloose/syntastic' "Syntax check
Plugin 'majutsushi/tagbar' "Browse the tags of the current file and get an overview of its structure.
Plugin 'ctrlpvim/ctrlp.vim' "Full path fuzzy file, buffer, mru, tag, ... finder for Vim.
Plugin 'bling/vim-airline' "Statusbar 
Plugin 'tpope/vim-fugitive' "Vim wrapper, allow for git viewing in airline
Plugin 'vim-airline/vim-airline-themes' "Airline themes
Plugin 'burnettk/vim-angular' " Angular functionality
Plugin 'jiangmiao/auto-pairs' "Insert or delete brackets, parens, quotes in pair
Plugin 'Yggdroot/indentLine' "A vim plugin to display the indention levels with thin vertical lines
Plugin 'altercation/vim-colors-solarized' "Solarized color theme
Plugin 'chriskempson/vim-tomorrow-theme' "Tomorrow color theme
Plugin 'w0ng/vim-hybrid' "Hybrid Solarized, codecademy, Jellybeans, and Tomorrow-Night
Plugin 'Valloric/YouCompleteMe' " Code completion 
Plugin 'marijnh/tern_for_vim' "Vim tern usage, greatly improves YouCompleteMe JS use
Plugin 'elzr/vim-json' " JSON highlighting 
Plugin 'rking/ag.vim' " Advanced searching 
" For JavaScript development
Plugin 'pangloss/vim-javascript' "Javascript syntax highlighting
Plugin 'moll/vim-node' " Node js 
Plugin 'groenewege/vim-less' " Less syntax
Plugin 'othree/javascript-libraries-syntax.vim' " Javascript syntax library highlighting
Plugin 'othree/jspc.vim' " Javascript parameter completion
Plugin '1995eaton/vim-better-javascript-completion' " Expansion of vim's javascript syntax file for HTML5



--regex-less=/^[ t ]*.([A-Za-z0-9_-]+)/1/c,class,classes/
--regex-less=/^[ t ]*#([A-Za-z0-9_-]+)/1/i,id,ids/
--regex-less=/^[ t ]*(([A-Za-z0-9_-]+[ tn, ]+)+){/1/t,tag,tags/
--regex-less=/^[ t ]*@medias+([A-Za-z0-9_-]+)/1/m,media,medias/


--regex-js=/[ \t. ]([A-Z][A-Z0-9._$]+)[ \t ]*[=:][ \t ]*([0-9"'\[\{]|null)/\1/n,constant/
--regex-js=/\.([A-Za-z0-9._$]+)[ \t ]*=[ \t ]*\{/\1/o,object/
--regex-js=/['"]*([A-Za-z0-9_$]+)['"]*[ \t ]*:[ \t ]*\{/\1/o,object/
--regex-js=/([A-Za-z0-9._$]+)\[["']([A-Za-z0-9_$]+)["']\][ \t ]*=[ \t ]*\{/\1\.\2/o,object/

--regex-js=/([A-Za-z0-9._$]+)[ \t ]*=[ \t ]*\(function\(\)/\1/c,class/
--regex-js=/['"]*([A-Za-z0-9_$]+)['"]*:[ \t ]*\(function\(\)/\1/c,class/
--regex-js=/class[ \t ]+([A-Za-z0-9._$]+)[ \t ]*/\1/c,class/
--regex-js=/([A-Za-z$][A-Za-z0-9_$()]+)[ \t ]*=[ \t ]*[Rr]eact.createClass[ \t ]*\(/\1/c,class/
--regex-js=/([A-Z][A-Za-z0-9_$]+)[ \t ]*=[ \t ]*[A-Za-z0-9_$]*[ \t ]*[{(]/\1/c,class/
--regex-js=/([A-Z][A-Za-z0-9_$]+)[ \t ]*:[ \t ]*[A-Za-z0-9_$]*[ \t ]*[{(]/\1/c,class/

--regex-js=/([A-Za-z$][A-Za-z0-9_$]+)[ \t ]*=[ \t ]*function[ \t ]*\(/\1/f,function/
--regex-js=/(function)*[ \t ]*([A-Za-z$_][A-Za-z0-9_$]+)[ \t ]*\([^)]*\)[ \t ]*\{/\2/f,function/
--regex-js=/['"]*([A-Za-z$][A-Za-z0-9_$]+)['"]*:[ \t ]*function[ \t ]*\(/\1/m,method/
--regex-js=/([A-Za-z0-9_$]+)\[["']([A-Za-z0-9_$]+)["']\][ \t ]*=[ \t ]*function[ \t ]*\(/\2/m,method/

js file

    function ($, _, Backbone, ChartModel, ChartView, AnalyticsService) {
        'use strict';
        // not exactly like the full chart controller
        //jshint unused:false
        function log(msg) {
            console.log('%c' + msg, 'font-size:12pt; color: #30F');
        //jshint unused:true

        function newGuid(noDashes) {
            var guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            return noDashes ? guid.replace(/-/g, '') : guid;

        function parseJson(jsonString) {
            //our JSON may include long integer numbers that can have more digits
            // than the JS Number type supports. These numbers are all IDs so we
            // can convert them to strings before parsing.
            var massagedJson = jsonString.replace(/([^\\]{1})\":(?: {0,})([0-9]{16,})([^\.])/g, "$1\":\"$2\"$3");
            //JSON docs are not supposed to have NaN or nan but some of our servers are adding them anyway.
            massagedJson = massagedJson.replace(/([^\\]{1})\":(?: {0,})(nan)/gi, "$1\":\"NaN\"");
            return JSON.parse(massagedJson);

        function processHistoricalData(data) {
            if(this.isDestroyed) {

            var bars = data.bars,
                subscription = this.subscriptions[data.subscriptionId];

            if (_.isUndefined(this.earliestTickReceived) && data.requestIsFinal) {
                this.model.set('lastError', 'Contract has no historical data');
                this.model.set('noHistoricalData', true);

                'requestIsPending': data.requestIsPending,
                'requestIsFinal': data.requestIsFinal,
                'requestIsCompleted': data.requestIsCompleted,
                'lastError': null,
                'retryingConnection': false,
                'remainingRetries': 5

            if (bars && bars.length > 0) {
                if (bars.length > 0) {
                    // The server can return an array of bars which only have inside market updates. In this
                    // case the converted bars array will be empty.
                    subscription.earliestTickReceived = new Date(bars[0].tickTime);
            } else {
                bars = [];


        function processLiveData(data) {
            if(this.isDestroyed) {

        function isVAP(name) {
            return (name.indexOf('VAP') > -1);

        var Controller = function (Host, $chartEl) {
            var self = this,
                uniqueId = null,
                onErrorHandler = null,
                onClosedHandler = null,
                onConnectedHandler = null,
                onHistoricalDataHandler = null,
                onLiveDataHandler = null;
        this.analyticsService = new AnalyticsService({
            analyticsUrl: analyticsUrl,
            accessToken: Host.getAuthToken(),
            logDebug: function () {
                console.log.apply(console, arguments);

        this.model = new ChartModel();
        uniqueId = this.model.getUniqueId();
        this.view = new ChartView({
            model: this.model,
            el: $chartEl[0],
            $el: $chartEl,
            controller: this

        onErrorHandler = function (err) {
            self.model.set('retryingConnection', false);
            self.model.set('firstHistoricalCallbackIteration', true);
            self.model.set('retryingConnection', false);

            if (err && err.error) {
                self.model.set('lastError', err.error);
                self.Host.log('Analytics Server Error: ' + err.error);
            else {
                self.model.set('lastError', 'Analytics Server Error');
                self.Host.log('Analytics Server Error, unspecified reason');

Thanks all and please let me know if I need to provide any additional details.

  • 1
    1. The Tagbar plugin reads the output of ctags-like programs directly, without using any pre-existing tags file, and processes that data independently. Everything tags-related it does or doesn't is completely separated from anything tag-related Vim does or doesn't. So… is your question about Vim or about Tagbar?
    – romainl
    Commented May 28, 2016 at 19:22
  • 2. You mention Universal Ctags, JSCtags, and the mostly unrelated tern_for_vim, but you show a convoluted JSCtags example and your ~/.ctags file. So… is your question about JSCtags or about Universal Ctags?
    – romainl
    Commented May 28, 2016 at 19:23
  • 3. Vim expects your tags file to adhere to a specific format described in :help ctags. Does the output of your shell command fit that format?
    – romainl
    Commented May 28, 2016 at 19:24
  • 4. You can use :echo taglist(".") to output what Vim could put together from the files in tagfiles(). Can you find your objects/methods, there?
    – romainl
    Commented May 28, 2016 at 19:24
  • @romainl. Thanks for the thoughts and sorry for the confusing information. I'm new to tagging in vim :-). Overall the objective is how to get tags to show up in Tagbar so I can have an overview and easy navigation of my file. Universal ctags and jsctags are apparently used by tagbar to auto-generate the tags (at least that's what I understood). If that's not possible then to use Vim's native tag functionality.
    – Grant A
    Commented May 31, 2016 at 14:04


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.