Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Add support for tag extension fields #13

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,14 @@ Get all tags matching the tag specified from the tags file at the path.
(default: `false`)

* `callback` - The function to call when complete with an error as the first
argument and an array containing objects that have `name` and
`file` keys and optionally a `pattern` key if the tag file
specified contains tag patterns.
argument and an array containing tag objects. Each tag object contains:

* `name` - name of the tag
* `file` - location of the tag
* `kind` - kind of the tag (see `ctags --list-kinds`)
* `lineNumber` - line number of the tag in `file` (defaults to 0 if not provided)
* `pattern` (optional) - pattern to search for in `file` (only if provided in tag file)
* `fields` (optional) - object with string values; extra fields for the tag (only if provided in tag file)

#### Example

Expand Down
54 changes: 54 additions & 0 deletions spec/ctags-fields-spec.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
path = require 'path'
ctags = require '../lib/ctags'

describe 'ctags', ->
tagsFile = null

beforeEach ->
tagsFile = path.join(__dirname, 'fixtures', 'fields-tags')

describe '.createReadStream(tagsFileWithFields)', ->
it 'returns a stream that emits data and end events', ->
stream = ctags.createReadStream(tagsFile)

tags = []
stream.on 'data', (chunk) -> tags = tags.concat(chunk)

endHandler = jasmine.createSpy('endHandler')
stream.on('end', endHandler)

waitsFor ->
endHandler.callCount is 1

runs ->
expect(tags.length).toBe 4

expect(tags[0].file).toBe 'tagged.js'
expect(tags[0].name).toBe 'callMeMaybe'
expect(tags[0].pattern).toBe '/^function callMeMaybe() {$/'
expect(tags[0].kind).toBe 'f'
expect(tags[0].fields).toEqual
class: 'TestClass'
extra: 'test'

expect(tags[1].file).toBe 'tagged-duplicate.js'
expect(tags[1].name).toBe 'duplicate'
expect(tags[1].pattern).toBe '/^function duplicate() {$/'
expect(tags[1].kind).toBe 'f'
expect(tags[1].fields).toEqual
test: 'spaces are fine'
test2: 'more spaces'

expect(tags[2].file).toBe 'tagged.js'
expect(tags[2].name).toBe 'duplicate'
expect(tags[2].pattern).toBe '/^function duplicate() {$/'
expect(tags[2].kind).toBe 'f'
expect(tags[2].fields).toEqual
'field name': '1'

expect(tags[3].file).toBe 'tagged.js'
expect(tags[3].name).toBe 'thisIsCrazy'
expect(tags[3].pattern).toBe '/^var thisIsCrazy = true;$/'
expect(tags[3].kind).toBe 'v'
expect(tags[3].fields).toEqual
emptyField: ''
10 changes: 10 additions & 0 deletions spec/fixtures/fields-tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
!_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 //
callMeMaybe tagged.js /^function callMeMaybe() {$/;" f class:TestClass extra:test
duplicate tagged-duplicate.js /^function duplicate() {$/;" f test:spaces are fine test2:more spaces
duplicate tagged.js /^function duplicate() {$/;" f field name:1
thisIsCrazy tagged.js /^var thisIsCrazy = true;$/;" v emptyField:
15 changes: 1 addition & 14 deletions src/tag-finder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,7 @@ void TagFinder::HandleOKCallback() {

Local<Array> array = Nan::New<Array>(matches.size());
for (size_t i = 0; i < matches.size(); i++) {
Local<Object> tagObject = Nan::New<Object>();
tagObject->Set(Nan::New<String>("name").ToLocalChecked(),
Nan::New<String>(matches[i].name.data()).ToLocalChecked());
tagObject->Set(Nan::New<String>("file").ToLocalChecked(),
Nan::New<String>(matches[i].file.data()).ToLocalChecked());
tagObject->Set(Nan::New<String>("kind").ToLocalChecked(),
Nan::New<String>(matches[i].kind.data()).ToLocalChecked());
tagObject->Set(Nan::New<String>("lineNumber").ToLocalChecked(),
Nan::New<Integer>((int32_t)matches[i].lineNumber));
if (matches[i].pattern.length() > 0)
tagObject->Set(
Nan::New<String>("pattern").ToLocalChecked(),
Nan::New<String>(matches[i].pattern.data()).ToLocalChecked());
array->Set(i, tagObject);
array->Set(i, matches[i].toV8Object());
}

Local<Value> argv[] = { Nan::Null(), array };
Expand Down
18 changes: 1 addition & 17 deletions src/tag-reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,7 @@ void TagReader::HandleOKCallback() {

Local<Array> array = Nan::New<Array>(tags.size());
for (size_t i = 0; i < tags.size(); i++) {
Local<Object> tagObject = Nan::New<Object>();
tagObject->Set(
Nan::New<String>("name").ToLocalChecked(),
Nan::New<String>(tags[i].name.data()).ToLocalChecked());
tagObject->Set(
Nan::New<String>("file").ToLocalChecked(),
Nan::New<String>(tags[i].file.data()).ToLocalChecked());
tagObject->Set(
Nan::New<String>("kind").ToLocalChecked(),
Nan::New<String>(tags[i].kind.data()).ToLocalChecked());
tagObject->Set(Nan::New<String>("lineNumber").ToLocalChecked(),
Nan::New<Integer>((int32_t)tags[i].lineNumber));
if (tags[i].pattern.length() > 0)
tagObject->Set(
Nan::New<String>("pattern").ToLocalChecked(),
Nan::New<String>(tags[i].pattern.data()).ToLocalChecked());
array->Set(i, tagObject);
array->Set(i, tags[i].toV8Object());
}

Local<Value> argv[] = { Nan::Null(), array };
Expand Down
37 changes: 37 additions & 0 deletions src/tag.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
#define SRC_TAG_H_

#include <string>
#include <utility>
#include <vector>
#include "nan.h"
#include "readtags.h"
#include "v8.h"

class Tag {
public:
Expand All @@ -12,13 +16,46 @@ class Tag {
kind = entry.kind != NULL ? entry.kind : "";
pattern = entry.address.pattern != NULL ? entry.address.pattern : "";
lineNumber = entry.address.lineNumber;
for (size_t i = 0; i < entry.fields.count; i++) {
fields.push_back(std::make_pair(
std::string(entry.fields.list[i].key),
std::string(entry.fields.list[i].value)
));
}
}

v8::Local<v8::Object> toV8Object() {
using namespace v8;

Local<Object> tagObject = Nan::New<Object>();
tagObject->Set(Nan::New<String>("name").ToLocalChecked(),
Nan::New<String>(name.data()).ToLocalChecked());
tagObject->Set(Nan::New<String>("file").ToLocalChecked(),
Nan::New<String>(file.data()).ToLocalChecked());
tagObject->Set(Nan::New<String>("kind").ToLocalChecked(),
Nan::New<String>(kind.data()).ToLocalChecked());
tagObject->Set(Nan::New<String>("lineNumber").ToLocalChecked(),
Nan::New<Integer>((int32_t)lineNumber));
if (pattern.length() > 0)
tagObject->Set(Nan::New<String>("pattern").ToLocalChecked(),
Nan::New<String>(pattern.data()).ToLocalChecked());
if (fields.size() > 0) {
Local<Object> fieldsObj = Nan::New<Object>();
for (size_t j = 0; j < fields.size(); j++) {
fieldsObj->Set(Nan::New<String>(fields[j].first).ToLocalChecked(),
Nan::New<String>(fields[j].second).ToLocalChecked());
}
tagObject->Set(Nan::New<String>("fields").ToLocalChecked(), fieldsObj);
}
return tagObject;
}

std::string name;
std::string file;
std::string kind;
std::string pattern;
unsigned long lineNumber;
std::vector<std::pair<std::string, std::string> > fields;
};

#endif // SRC_TAG_H_