Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Positional info [SL-2197] #106

Merged
merged 11 commits into from
Mar 26, 2019
Merged

Positional info [SL-2197] #106

merged 11 commits into from
Mar 26, 2019

Conversation

P0lip
Copy link
Contributor

@P0lip P0lip commented Mar 22, 2019

Please check if the PR fulfills these requirements

  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

What kind of change does this PR introduce?

fixes #92

Range is now attached to a single validation object, therefore it's possible to see where the issue/error actually occurs in a file (when using Spectral using API)
Moreover, CLI makes use of them to display line and column as shown in the screenshot below

image

What is the current behavior?

Range is not included (API usage) and positional info is not displayed (CLI usage).

image

If this is a feature change, what is the new behavior?

See the screenshot above.

Does this PR introduce a breaking change?

No changes required!

Other information

(e.g. detailed explanation, related issues, links for us to have context, eg. stackoverflow, issues outside of the repo, forum, etc.)

@philsturgeon
Copy link
Contributor

RE the chat we had elsewhere about showing file path or JSON path: I think we should include both in JSON, and only show file path in the CLI. Most users will be really excited to be able to CTRL/CMD+Click the file path and open it right up. If folk need the path information it will be available in JSON and other formats as we grow them out, and if users request we find a way to include it in stylish we can do that later.

Does that sound good?

@P0lip
Copy link
Contributor Author

P0lip commented Mar 25, 2019

@philsturgeon
Sounds good to me!
I'm adding filenames at the very moment :)

@philsturgeon
Copy link
Contributor

philsturgeon commented Mar 25, 2019 via email

@philsturgeon philsturgeon mentioned this pull request Mar 25, 2019
2 tasks
@P0lip P0lip changed the base branch from master to release/2.0 March 25, 2019 14:36
src/spectral.ts Outdated
@@ -25,9 +26,9 @@ export class Spectral {
this.resolver = opts && opts.resolver ? opts.resolver : new Resolver();
}

public async run(target: object): Promise<IRuleResult[]> {
public async run(target: object, parserMeta?: IParserMeta): Promise<IRuleResult[]> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parserMeta is needed to calculate lines and columns for given JSON Path.

Ideally, we could have a single argument accepting IParserResult, but that would be breaking and would force users to use parseWithPointers functions.

If parserMeta is provided, range property (lines and columns) is attached to a single result and hence can be displayed in the output (CLI) or consumed programmatically (API).

@philsturgeon @tbarn your thoughts on that?
We will need to document it for sure.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this thing for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, IRange (range property) follows LSP standard and hence lines and characters (columns) are 0-based.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philsturgeon It's needed by getJsonPathForPosition

Copy link
Contributor Author

@P0lip P0lip Mar 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know the above looks slightly weird, but it preserves the old behavior.
It simply degrades gracefully - if no parserMeta is provided, all validation results will still have all the properties except range (and source - name of the file).

We could have a single argument, but we'd force user to use parseWithPointers function in such case, which is something we most likely want to avoid.
The other possible solution is to have another method, i.e. runParsed or similar, but IMHO this would introduce even bigger confusion to end-users.

@tbarn @philsturgeon @marbemac thoughts?
Note, The solution is also briefly described in the readme above.
Let me know what your thoughts are.

Edit:
Alternatively, we could also make run accept strings only (that'd be my favorite solution)

Copy link
Contributor Author

@P0lip P0lip Mar 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sounds good to me.
What about S^2 (I hope ya know what I mean)? I'd like to avoid reparsing as much as possible in this particular case as this may degrade performance.

Apart from that, we'd still need to figure out some solution for CLI. CLI passes an already parsed content and I assume the result of safeStringify wouldn't equal the actual content of a file.

Copy link
Contributor Author

@P0lip P0lip Mar 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or did you mean to have the following implementation?

run(target: IParserAstResult | object) {
  // if plain object, reparse
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about something like this?

Not sure we even need to document the IParserAstResult right now - can use internally and from studio for now.

run(target: IParserAstResult | object | string) {
  let parsed = target;
  if (!isParserAstResult(target)) {
    parsed = parseWithPointers(safeStringify(target));
  }

  // ...resolve parsed, etc
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above looks good to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah having it there but hidden from docs is fine with me.

@P0lip P0lip marked this pull request as ready for review March 25, 2019 14:46
@P0lip P0lip requested a review from tbarn March 25, 2019 14:46
@P0lip P0lip self-assigned this Mar 25, 2019
@tbarn
Copy link
Contributor

tbarn commented Mar 25, 2019

I'm getting an error when I do yarn build:

➜  spectral git:(feat/positional-info) yarn build
yarn run v1.13.0
$ sl-scripts build && oclif-dev manifest && node ./scripts/prepare-cli
building......
src/linter.ts:6:10 - error TS2305: Module '"../../../../../Users/taylorbarnett/Stoplight/spectral/node_modules/@stoplight/yaml"' has no exported member 'getLocationForJsonPath'.

6 import { getLocationForJsonPath } from '@stoplight/yaml';
           ~~~~~~~~~~~~~~~~~~~~~~


Found 1 error.
building...... done
error Command failed with exit code 2.

@P0lip
Copy link
Contributor Author

P0lip commented Mar 25, 2019

@tbarn make sure to run yarn before (dependencies have been updated).

@tbarn
Copy link
Contributor

tbarn commented Mar 25, 2019

@P0lip I did but I missed that it had an error too:

error sane@4.1.0: The engine "node" is incompatible with this module. Expected version "6.* || 8.* || >= 10.*". Got "9.11.2"

what version of node should I be using?

@P0lip
Copy link
Contributor Author

P0lip commented Mar 25, 2019

@tbarn
Either 8 or 10 (recommended as it's the latest LTS).
(alternatively, you can do yarn --ignore-engines)

In any case, I don't see a reason not to support Node 9, therefore, the issue you expected should be fixed.
We might need to find out why sane doesn't accept Node 6 and node 9.

@tbarn
Copy link
Contributor

tbarn commented Mar 25, 2019

@P0lip I probably set my nvm to v9 to test something else. I just want to make sure we have these kinds of things recorded in the contributing doc for others.

I got it running, tested out a few different spec documents, and it looks great to me.

@P0lip
Copy link
Contributor Author

P0lip commented Mar 25, 2019

@tbarn
Did yarn --ignore-engines work for you?
If so, it's a good candidate to be listed in some troubleshooting section until we fix the issue.

@tbarn
Copy link
Contributor

tbarn commented Mar 25, 2019

Shared elsewhere, but I wanted to share here too. The bump on the stoplight/yaml package fixed the problem on v9.

README.md Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
src/spectral.ts Outdated
public async run(target: object): Promise<IRuleResult[]> {
const resolvedTarget = (await this.resolver.resolve(target)).result;
return runRules(target, this.rules, this.functions, { resolvedTarget });
public async run(target: IParserMeta | object | string): Promise<IRuleResult[]> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of messing with the target argument, could we possibly add a second optional argument for this stuff?

then we can throw warnings saying "We cannot give you any positional information because you didnt give us enough information" and the output can be ?? or something? Does that make any sense?

Copy link
Contributor Author

@P0lip P0lip Mar 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we had that done this way initially but was changed as discussed here #106 (comment)
In general, I'm not opinionated on that at all and I'm happy to go with whatever is more intuitive for users.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, my comment saying Yeah having it there but hidden from docs is fine with me. was written from mobile and I hadn't seen marcs suggestion.

Ok, so the name IParserMeta is whats throwing me. Marcs example makes it clear that it can either be a parsed AST object, or a generic object, and if its the generic object then its not gonna work with line numbers and stuff. That's all fine if we use a name a bit more like his.

Metadata is usually a different thing to data, so if it has meta in the name I'd expect it to be a different argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philsturgeon
Yeah, naming is certainly not my strongest asset 😆

I can name it IParsedAst if it makes everything more clear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to IParsedResult since it also has some different properties than just ast, so this could be confusing.
Let me know what you think @philsturgeon

Copy link
Contributor

@marbemac marbemac Mar 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and if its the generic object then its not gonna work with line numbers and stuff

It should still work if go with original suggestion. Basically, if it's a plain object we stringily it and parse it with our parser to get positional info:

run(target: IParserAstResult | object | string) {
  let parsed = target;
  if (!isParserAstResult(target)) {
    parsed = parseWithPointers(safeStringify(target));
  }

  // ...resolve parsed, etc
}

So no compromise for end user passing in plain object or stringified object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we reparse it therefore some ranges are attached. Initially, when we didn't reparse the object, ranges were missing.

@philsturgeon
Copy link
Contributor

philsturgeon commented Mar 26, 2019 via email

@P0lip P0lip merged commit 5d52318 into release/2.0 Mar 26, 2019
@P0lip P0lip deleted the feat/positional-info branch March 26, 2019 16:15
philsturgeon pushed a commit that referenced this pull request Mar 28, 2019
* feat: implement positional info

* feat: show filepath

* test: update snapshots

* feat: use resolve instead of join

* fix: show message if rule summary is missing

* chore: document code, update readme and rename uri to source

* refactor: exclude source if undefined

* refactor: enhance run

* feat: sort results by line

* fix: summary is yellow in some cases

* feat: rename IParsedResult -> IParsedResult
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants