/// <summary>
        /// Registers a change in the text buffer for the language service. The processing of the change is asynchronous, so it does not block the caller.
        /// </summary>
        public void TextBufferChanged(GherkinTextBufferChange change)
        {
            if (isDisposed)
                throw new ObjectDisposedException("GherkinLanguageService");

            var task = new ParsingTask(this, change);

            projectScope.GherkinProcessingScheduler.EnqueueParsingRequest(task);
//            task.Apply(); // synchronous execution
        }
        private GherkinFileScopeChange GetInvalidDialectScopeChange(GherkinTextBufferChange change)
        {
            var fileScope = new GherkinFileScope(null, change.ResultTextSnapshot)
                                {
                                    InvalidFileEndingBlock = new InvalidFileBlock(0, 
                                        change.ResultTextSnapshot.LineCount - 1,
                                        new ErrorInfo("Invalid Gherkin dialect!", 0, 0, null))
                                };

            return GherkinFileScopeChange.CreateEntireScopeChange(fileScope);
        }
        public static GherkinTextBufferChange Merge(GherkinTextBufferChange change1, GherkinTextBufferChange change2)
        {
            Debug.Assert(change1.ResultTextSnapshot.Version.VersionNumber <= change2.ResultTextSnapshot.Version.VersionNumber);
            if (change1.Type == GherkinTextBufferChangeType.EntireFile || change2.Type == GherkinTextBufferChangeType.EntireFile)
                return CreateEntireBufferChange(change2.ResultTextSnapshot);

            return new GherkinTextBufferChange(
                change1.Type == GherkinTextBufferChangeType.MultiLine || change2.Type == GherkinTextBufferChangeType.MultiLine ? GherkinTextBufferChangeType.MultiLine : GherkinTextBufferChangeType.SingleLine, 
                Math.Min(change1.StartLine, change2.StartLine),
                Math.Max(change1.EndLine, change2.EndLine),
                Math.Min(change1.StartPosition, change2.StartPosition),
                Math.Max(change1.EndPosition, change2.EndPosition),
                change1.LineCountDelta + change2.LineCountDelta,
                change1.PositionDelta + change2.PositionDelta,
                change2.ResultTextSnapshot);
        }
예제 #4
0
            public IGherkinProcessingTask Merge(IGherkinProcessingTask other)
            {
                if (other is PingTask)
                {
                    return(this);
                }

                ParsingTask otherParsingTask = other as ParsingTask;

                if (otherParsingTask == null || languageService != otherParsingTask.languageService)
                {
                    return(null);
                }

                return(new ParsingTask(
                           languageService, GherkinTextBufferChange.Merge(change, otherParsingTask.change)));
            }
        private GherkinFileScopeChange PartialParse(GherkinTextBufferChange change, IGherkinFileScope previousScope)
        {
            visualStudioTracer.Trace("Start incremental parsing", ParserTraceCategory);
            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Start();

            partialParseCount++;

            var textSnapshot = change.ResultTextSnapshot;

            IScenarioBlock firstAffectedScenario = GetFirstAffectedScenario(change, previousScope);

            Debug.Assert(firstAffectedScenario != null);
            int parseStartPosition = textSnapshot.GetLineFromLineNumber(firstAffectedScenario.GetStartLine()).Start;

            string fileContent = textSnapshot.GetText(parseStartPosition, textSnapshot.Length - parseStartPosition);

            var gherkinListener = new GherkinTextBufferPartialParserListener(
                previousScope.GherkinDialect,
                textSnapshot, projectScope.Classifications,
                previousScope,
                change.EndLine, change.LineCountDelta);

            var scanner = new GherkinScanner(previousScope.GherkinDialect, fileContent, firstAffectedScenario.GetStartLine());

            IScenarioBlock firstUnchangedScenario = null;

            try
            {
                scanner.Scan(gherkinListener);
            }
            catch (PartialListeningDoneException partialListeningDoneException)
            {
                firstUnchangedScenario = partialListeningDoneException.FirstUnchangedScenario;
            }

            var partialResult = gherkinListener.GetResult();

            var result = MergePartialResult(previousScope, partialResult, firstAffectedScenario, firstUnchangedScenario, change.LineCountDelta);

            stopwatch.Stop();
            TraceFinishParse(stopwatch, "incremental", result);
            return(result);
        }
예제 #6
0
        public static GherkinTextBufferChange Merge(GherkinTextBufferChange change1, GherkinTextBufferChange change2)
        {
            Debug.Assert(change1.ResultTextSnapshot.Version.VersionNumber <= change2.ResultTextSnapshot.Version.VersionNumber);
            if (change1.Type == GherkinTextBufferChangeType.EntireFile || change2.Type == GherkinTextBufferChangeType.EntireFile)
            {
                return(CreateEntireBufferChange(change2.ResultTextSnapshot));
            }

            return(new GherkinTextBufferChange(
                       change1.Type == GherkinTextBufferChangeType.MultiLine || change2.Type == GherkinTextBufferChangeType.MultiLine ? GherkinTextBufferChangeType.MultiLine : GherkinTextBufferChangeType.SingleLine,
                       Math.Min(change1.StartLine, change2.StartLine),
                       Math.Max(change1.EndLine, change2.EndLine),
                       Math.Min(change1.StartPosition, change2.StartPosition),
                       Math.Max(change1.EndPosition, change2.EndPosition),
                       change1.LineCountDelta + change2.LineCountDelta,
                       change1.PositionDelta + change2.PositionDelta,
                       change2.ResultTextSnapshot));
        }
        public GherkinFileScopeChange Parse(GherkinTextBufferChange change, IGherkinFileScope previousScope = null)
        {
            var gherkinDialect = GetGherkinDialect(change.ResultTextSnapshot);
            if (gherkinDialect == null)
                return GetInvalidDialectScopeChange(change);

            bool fullParse = false;
            if (previousScope == null)
                fullParse = true;
            else if (!Equals(previousScope.GherkinDialect, gherkinDialect))
                fullParse = true;
            else if (partialParseCount >= PartialParseCountLimit)
                fullParse = true;
            else if (GetFirstAffectedScenario(change, previousScope) == null)
                fullParse = true;

            if (fullParse)
                return FullParse(change.ResultTextSnapshot, gherkinDialect);

            return PartialParse(change, previousScope);
        }
        private IScenarioBlock GetFirstAffectedScenario(GherkinTextBufferChange change, IGherkinFileScope previousScope)
        {
            if (change.Type == GherkinTextBufferChangeType.SingleLine)
                //single-line changes on the start cannot influence the previous scenario
                return previousScope.ScenarioBlocks.LastOrDefault(s => s.GetStartLine() <= change.StartLine);

            // if multiple lines are added at the first line of a block, it can happen that these lines will belong
            // to the previous block
            return previousScope.ScenarioBlocks.LastOrDefault(s => s.GetStartLine() < change.StartLine); 
        }
        private GherkinFileScopeChange PartialParse(GherkinTextBufferChange change, IGherkinFileScope previousScope)
        {
            visualStudioTracer.Trace("Start incremental parsing", ParserTraceCategory);
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            partialParseCount++;

            var textSnapshot = change.ResultTextSnapshot;

            IScenarioBlock firstAffectedScenario = GetFirstAffectedScenario(change, previousScope);
            Debug.Assert(firstAffectedScenario != null);
            int parseStartPosition = textSnapshot.GetLineFromLineNumber(firstAffectedScenario.GetStartLine()).Start;

            string fileContent = textSnapshot.GetText(parseStartPosition, textSnapshot.Length - parseStartPosition);

            var gherkinListener = new GherkinTextBufferPartialParserListener(
                previousScope.GherkinDialect,
                textSnapshot, projectScope.Classifications, 
                previousScope, 
                change.EndLine, change.LineCountDelta);

            var scanner = new GherkinScanner(previousScope.GherkinDialect, fileContent, firstAffectedScenario.GetStartLine());

            IScenarioBlock firstUnchangedScenario = null;
            try
            {
                scanner.Scan(gherkinListener);
            }
            catch (PartialListeningDoneException partialListeningDoneException)
            {
                firstUnchangedScenario = partialListeningDoneException.FirstUnchangedScenario;
            }

            var partialResult = gherkinListener.GetResult();

            var result = MergePartialResult(previousScope, partialResult, firstAffectedScenario, firstUnchangedScenario, change.LineCountDelta);
            stopwatch.Stop();
            TraceFinishParse(stopwatch, "incremental", result);
            return result;
        }
예제 #10
0
 public ParsingTask(GherkinLanguageService languageService, GherkinTextBufferChange change)
 {
     this.languageService = languageService;
     this.change = change;
 }
예제 #11
0
 public ParsingTask(GherkinLanguageService languageService, GherkinTextBufferChange change)
 {
     this.languageService = languageService;
     this.change          = change;
 }