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

fixed peek definition tokenizing #281

Merged
merged 9 commits into from
Mar 23, 2017
Merged

fixed peek definition tokenizing #281

merged 9 commits into from
Mar 23, 2017

Conversation

abist
Copy link
Contributor

@abist abist commented Mar 15, 2017

Fixes peek definition tokenizing.

The issue here was that the parser wasn't correctly getting the correct token position, thus failing Peek Definition if the cursor was right in the starting or the end of the object we want to look up. For example, Peek Definition would fail if, let's say, the cursor were anywhere outside this range - sys.d [atabase] s

According to the new way PeekDefinition works, instead of returning an error if an object isn't found, we first look at the children and parent tokens and return the definition of those. If nothing is defined, then we return the error.

@mention-bot
Copy link

@abist, thanks for your PR! By analyzing the history of the files in this pull request, we identified @kburtram, @sharonravindran and @llali to be potential reviewers.

@msftclas
Copy link

@abist,
Thanks for your contribution as a Microsoft full-time employee or intern. You do not need to sign a CLA.
Thanks,
Microsoft Pull Request Bot

@coveralls
Copy link

Coverage Status

Coverage increased (+0.03%) to 80.849% when pulling 4be0639 on task/PeekDefinitionTokenizer into 7ba2011 on dev.

@kevcunnane
Copy link
Contributor

@abist, could you clarify what this is actually fixing? Neither the issue nor this PR describe what the impact will be. For example: "fixes issue where Peek Definition fails to find the correct token position, resulting in failure to return results...." would be my guess? But this helps us prioritize reviewing and signoff since we know what we're looking for. Please also make sure this description is included on checkin so we can look back at commit history and see what was done. Thanks!

@abist
Copy link
Contributor Author

abist commented Mar 16, 2017

@kevcunnane Agreed, I updated the description.

Copy link
Contributor

@kevcunnane kevcunnane left a comment

Choose a reason for hiding this comment

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

Please add in tests to cover this scenario and capture what you're fixing - e.g. what failed before but would now pass? Also please fix the issue where PeekDefinition.GetScript now ends up adding +2 instead of +1 to the line (since you do it here, then PeekDefinition does too

/// <summary>
/// Returns the token that is used for Peek Definition objects
/// </summary>
internal static Token GetPeakDefinitionToken(ScriptParseInfo scriptParseInfo, int startLine, int startColumn)
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo: Peak -> Peek

@@ -814,12 +814,17 @@ internal DefinitionResult GetDefinition(TextDocumentPosition textDocumentPositio
bindingTimeout: LanguageService.PeekDefinitionTimeout,
bindOperation: (bindingContext, cancelToken) =>
{
string schemaName = this.GetSchemaName(scriptParseInfo, textDocumentPosition.Position, scriptFile);
// Handle tokenizing for Peek Definition
Position tokenPosition = new Position();
Copy link
Contributor

Choose a reason for hiding this comment

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

2 suggestions:

  • Move the Position creation up to before `Token selectedToken = ScriptDocumentInfo.GetPeekDefinitionToken) and use its line and character values. This minimizes the times we need to convert the line/character counts by adding 1 to them. Every time we do this, we risk an error later since we're tracking in multiple places
  • Use the constructor pattern as follow since it's a clean way to create the object :
Position tokenPosition = new Position
{
    Line = textDocumentPosition.Position.Line + 1,
    Character = textDocumentPosition.Position.Character + 1
};

// Script object using SMO
PeekDefinition peekDefinition = new PeekDefinition(bindingContext.ServerConnection, connInfo);
return peekDefinition.GetScript(
scriptParseInfo.ParseResult,
textDocumentPosition.Position,
tokenPosition,
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this an error? PeekDefinition.GetScript already adjusts line & character by 1. You need to alter that method to no longer do so.

@@ -808,18 +808,23 @@ internal DefinitionResult GetDefinition(TextDocumentPosition textDocumentPositio
{
try
{
// Handle tokenizing for Peek Definition
Position tokenPosition = new Position();
Copy link
Contributor

Choose a reason for hiding this comment

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

We have an internal class called BufferPosition, which tends to use the VSCode Position values but 1-indexed. Perhaps for clarity, we should change the code to use that? Otherwise it's kind of confusing since we're adjusting this but nobody would know why.

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 arguments strictly require the Position class, so I'm not sure if we can use the BufferPosition class here.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm suggesting changing the PeekDefinition class to use BufferPosition, since the pattern we have is that this uses Position's values + 1. That way it's clear what the expected input is. Otherwise, if someone else uses this class they might happily send in an unmodified Position object.

@@ -795,7 +795,7 @@ internal DefinitionResult GetDefinition(TextDocumentPosition textDocumentPositio
}

// Get token from selected text
Token selectedToken = ScriptDocumentInfo.GetToken(scriptParseInfo, textDocumentPosition.Position.Line + 1, textDocumentPosition.Position.Character);
Token selectedToken = ScriptDocumentInfo.GetPeakDefinitionToken(scriptParseInfo, textDocumentPosition.Position.Line + 1, textDocumentPosition.Position.Character + 1);
Copy link
Contributor

@benrr101 benrr101 Mar 16, 2017

Choose a reason for hiding this comment

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

Peek instead of Peak
Gold Peak Tea

@coveralls
Copy link

Coverage Status

Coverage increased (+0.06%) to 83.278% when pulling 194b228 on task/PeekDefinitionTokenizer into d7ecfb1 on dev.

@@ -796,76 +842,127 @@ internal DefinitionResult GetDefinition(TextDocumentPosition textDocumentPositio
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd put an isconnected check at the top and error out cleanly here.

@@ -796,76 +842,127 @@ internal DefinitionResult GetDefinition(TextDocumentPosition textDocumentPositio
}

// Get token from selected text
Token selectedToken = ScriptDocumentInfo.GetToken(scriptParseInfo, textDocumentPosition.Position.Line + 1, textDocumentPosition.Position.Character);
var selectedToken = ScriptDocumentInfo.GetPeekDefinitionTokens(scriptParseInfo, textDocumentPosition.Position.Line + 1, textDocumentPosition.Position.Character + 1);
Copy link
Contributor

Choose a reason for hiding this comment

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

DOn't use var here - it's a complicated data type so be clear. Otherwise those reading the code won't know what's happening.

{
try
var token = selectedToken.Item1.Pop();
Copy link
Contributor

Choose a reason for hiding this comment

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

Please define what Item1 is. Assign to a clear, named variable.

string tokenText = TextUtilities.RemoveSquareBracketSyntax(token.Text);
textDocumentPosition.Position.Line = token.StartLocation.LineNumber;
textDocumentPosition.Position.Character = token.StartLocation.ColumnNumber;
if (scriptParseInfo.IsConnected && Monitor.TryEnter(scriptParseInfo.BuildingMetadataLock))
Copy link
Contributor

Choose a reason for hiding this comment

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

Since you'll move the "not connected" error up, please add a separate error for when you fail to enter the monitor. Probably state "Timeout out waiting to query metadata from the server"

// then check the parents
foreach (var i in selectedToken.Item2.ToList())
{
var token = selectedToken.Item2.Dequeue();
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 Item2? What does it represent.

{
Stack<Token> childrenTokens = new Stack<Token>();
Queue<Token> parentTokens = new Queue<Token>();
if (scriptParseInfo != null && scriptParseInfo.ParseResult != null && scriptParseInfo.ParseResult.Script != null && scriptParseInfo.ParseResult.Script.Tokens != null)
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: put each if check on its own line

int currentIndex = 0;
foreach (var token in scriptParseInfo.ParseResult.Script.Tokens)
{

Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: remove unnecessary whitespace

/// <returns></returns>
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
Copy link
Contributor

Choose a reason for hiding this comment

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

What if someone has a subclass of position? I think IsInstanceOf or similar (we have cases of this in the project) work better. You could also just do obj as Position and check if null


// place the cursor on every token
//cursor on objects
TextDocumentPosition objectDocument = new TextDocumentPosition
Copy link
Contributor

Choose a reason for hiding this comment

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

Use a helper method to make these all 1-line definitions

[Fact]
public async void GetDefinitionFromChildrenAndParents()
{
string queryString = "select * from master.sys.objects";
Copy link
Contributor

Choose a reason for hiding this comment

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

At a minimum please add procedure-specific tests, and tests with and without escaping of brackets on each one. Ideally make it easy to define multiple inputs so future bug regressions can be tested (e.g. add in a framework that takes JSON file input defining the query & positions to test from, and what's expected to be returned at each). This 2nd ask is a stretch goal though - can ship without it.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.1%) to 83.366% when pulling b12ab7d on task/PeekDefinitionTokenizer into 16b3874 on dev.

private DefinitionResult GetDefinitionFromTokenList(TextDocumentPosition textDocumentPosition, List<Token> tokenList,
ScriptParseInfo scriptParseInfo, ScriptFile scriptFile, ConnectionInfo connInfo)
{

Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: minimize whitespace lines

@abist abist merged commit 9e8e1df into dev Mar 23, 2017
@abist abist deleted the task/PeekDefinitionTokenizer branch March 23, 2017 20:20
@kburtram kburtram mentioned this pull request Aug 29, 2017
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.

6 participants