/// <summary> /// Try to parse as a sequence of columns and styles separated by commas. /// e.g. 40 1px solid red, 80 2px dashed blue /// The style part is optional but, if present, it must be well-formed. /// </summary> /// <param name="codingConvention">The coding convention.</param> /// <param name="fallbackStrokeParameters">Stroke parameters to use when the style is not specified.</param> /// <returns>The set of guidelines if successful, or null if not.</returns> private static HashSet <Guideline> ParseGuidelines(string codingConvention, StrokeParameters fallbackStrokeParameters) { var set = new HashSet <Guideline>(); foreach (var token in GetTokens(codingConvention, s_comma)) { var partEnumerator = GetTokens(token, s_space); if (!partEnumerator.MoveNext()) { // Empty token. Ignore and continue. continue; } if (!TryParsePosition(partEnumerator.Current, out var column)) { return(null); } var strokeParameters = fallbackStrokeParameters; if (partEnumerator.MoveNext() && !TryParseStrokeParameters(partEnumerator, out strokeParameters)) { return(null); } set.Add(new Guideline(column, strokeParameters?.Freeze())); } return(set); }
/// <summary> /// Construct a new <see cref="Guideline"/>. /// </summary> /// <param name="column">The column number. Must be between 0 and 10,000.</param> /// <param name="strokeParameters">The stroke parameters for this guideline. If null, then /// the default brush from Fonts & Colors is used with default .</param> public Guideline(int column, StrokeParameters strokeParameters) { if (!IsValidColumn(column)) { throw new ArgumentOutOfRangeException(nameof(column), Resources.AddGuidelineParameterOutOfRange); } Column = column; StrokeParameters = strokeParameters; }
private static bool TryParseStrokeParameters(TokenEnumerator tokensEnumerator, out StrokeParameters strokeParameters) { strokeParameters = null; // Pixel width (stroke thickness) var token = tokensEnumerator.Current; if (!token.EndsWith("px", StringComparison.Ordinal)) { return(false); } if (!double.TryParse(token.Substring(0, token.Length - 2), out var strokeThickness)) { return(false); } if (strokeThickness < 0 || strokeThickness > 50) { return(false); } strokeParameters = new StrokeParameters { Brush = new SolidColorBrush(Colors.Black), StrokeThickness = strokeThickness }; if (!tokensEnumerator.MoveNext()) { return(true); } // Line style token = tokensEnumerator.Current; if (Enum.TryParse <LineStyle>(token, ignoreCase: true, out var lineStyle)) { strokeParameters.LineStyle = lineStyle; } if (!tokensEnumerator.MoveNext()) { return(true); } // Color token = tokensEnumerator.Current; if (TryParseColor(token, out var color)) { strokeParameters.Brush = new SolidColorBrush(color); } // Ignore trailing tokens. return(true); }
/// <summary> /// The guideline_style looks like this: /// guidelines_style = 1px dotted 80FF0000 /// Meaning single pixel, dotted style, color red, 50% opaque /// /// 1px specifies the width in pixels. /// dotted specifies the line style.Simple to support: solid, dotted and dashed /// </summary> /// <param name="text">The value read from guidelines_style editorconfig.</param> /// <param name="strokeParameters">The parsed stroke parameters.</param> /// <returns>True if parameters were parsed. False otherwise.</returns> public static bool TryParseStrokeParametersFromCodingConvention(string text, out StrokeParameters strokeParameters) { var tokensEnumerator = GetTokens(text, s_separators).GetEnumerator(); if (!tokensEnumerator.MoveNext()) { strokeParameters = null; return(false); } return(TryParseStrokeParameters(tokensEnumerator, out strokeParameters)); }
/// <summary> /// Creates editor column guidelines /// </summary> /// <param name="view">The <see cref="IWpfTextView"/> upon which the adornment will be drawn</param> /// <param name="settings">The guideline settings.</param> /// <param name="guidelineBrush">The guideline brush.</param> /// <param name="codingConventionsManager">The coding conventions manager for handling .editorconfig settings.</param> /// <param name="telemetry">Telemetry interface.</param> public ColumnGuideAdornment(IWpfTextView view, ITextEditorGuidesSettings settings, GuidelineBrush guidelineBrush, ICodingConventionsManager codingConventionsManager) { _view = view; _guidelineBrush = guidelineBrush; _guidelineBrush.BrushChanged += GuidelineBrushChanged; _strokeParameters = StrokeParameters.FromBrush(_guidelineBrush.Brush); if (codingConventionsManager != null && view.TryGetTextDocument(out var textDocument)) { _codingConventionsCancellationTokenSource = new CancellationTokenSource(); var fireAndForgetTask = LoadGuidelinesFromEditorConfigAsync(codingConventionsManager, textDocument.FilePath); } InitializeGuidelines(settings.GuideLinePositionsInChars); _view.LayoutChanged += OnViewLayoutChanged; _settingsChanged = settings as INotifyPropertyChanged; if (_settingsChanged != null) { _settingsChanged.PropertyChanged += SettingsChanged; } _view.Closed += ViewClosed; }
private Task UpdateGuidelinesFromCodingConventionAsync(ICodingConventionContext codingConventionContext, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return(Task.FromCanceled(cancellationToken)); } StrokeParameters strokeParameters = null; if (codingConventionContext.CurrentConventions.TryGetConventionValue("guidelines_style", out string guidelines_style)) { if (TryParseStrokeParametersFromCodingConvention(guidelines_style, out strokeParameters)) { _isUsingCodingConvention = true; strokeParameters.Freeze(); } } ICollection <Guideline> guidelines = null; if (codingConventionContext.CurrentConventions.TryGetConventionValue("guidelines", out string guidelinesConventionValue)) { guidelines = ParseGuidelinesFromCodingConvention(guidelinesConventionValue, strokeParameters); } // Also support max_line_length: https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#max_line_length if (codingConventionContext.CurrentConventions.TryGetConventionValue("max_line_length", out string max_line_length) && TryParsePosition(max_line_length, out int maxLineLengthValue)) { (guidelines ?? (guidelines = new List <Guideline>())).Add(new Guideline(maxLineLengthValue, strokeParameters)); } if (guidelines != null) { // Override 'classic' settings. _isUsingCodingConvention = true; // TODO: await JoinableTaskFactory.SwitchToMainThreadAsync(); #pragma warning disable VSTHRD001 // Avoid legacy thread switching APIs _ = _view.VisualElement.Dispatcher.BeginInvoke(new Action <IEnumerable <Guideline> >(GuidelinesChanged), guidelines); #pragma warning restore VSTHRD001 // Avoid legacy thread switching APIs } if (_isUsingCodingConvention && !s_sentEditorConfigTelemetry) { var eventTelemetry = new EventTelemetry("EditorConfig"); if (!string.IsNullOrEmpty(guidelinesConventionValue)) { eventTelemetry.Properties.Add("Convention", guidelinesConventionValue); } if (!string.IsNullOrEmpty(max_line_length)) { eventTelemetry.Properties.Add(nameof(max_line_length), max_line_length); } if (!string.IsNullOrEmpty(guidelines_style)) { eventTelemetry.Properties.Add(nameof(guidelines_style), guidelines_style); } ColumnGuideAdornmentFactory.AddGuidelinesToTelemetry(eventTelemetry, guidelines); Telemetry.Client.TrackEvent(eventTelemetry); s_sentEditorConfigTelemetry = true; } return(Task.CompletedTask); }
public static HashSet <Guideline> ParseGuidelinesFromCodingConvention(string codingConvention, StrokeParameters fallbackStrokeParameters) { // First try parsing as a sequence of columns and styles separated by commas. var result = ParseGuidelines(codingConvention, fallbackStrokeParameters); if (result != null) { return(result); } // Fall back to parsing as just a set of column positions, ignoring any unparsable values. result = new HashSet <Guideline>(); foreach (var position in GetTokens(codingConvention, s_separators)) { if (TryParsePosition(position, out int column)) { result.Add(new Guideline(column, fallbackStrokeParameters)); } } return(result); }