private string?GetSelectedTextFromFile(DocumentUri uri, Range?range) { if (range is null) { return(null); } var contents = File.ReadAllText(uri.GetFileSystemPath()); var lineStarts = TextCoordinateConverter.GetLineStarts(contents); var start = TextCoordinateConverter.GetOffset(lineStarts, range.Start.Line, range.Start.Character); var end = TextCoordinateConverter.GetOffset(lineStarts, range.End.Line, range.End.Character); var selectedText = contents.Substring(start, end - start); return(selectedText); }
// If the insertion path already exists, or can't be added (eg array instead of object exists on the path), returns null public (int line, int column, string insertText)? InsertIfNotExist(string[] propertyPaths, object valueIfNotExist) { if (propertyPaths.Length == 0) { throw new ArgumentException($"{nameof(propertyPaths)} must not be empty"); } if (string.IsNullOrWhiteSpace(_json)) { return(AppendToEndOfJson(Stringify(PropertyPathToObject(propertyPaths, valueIfNotExist)))); } TextReader textReader = new StringReader(_json); JsonReader jsonReader = new JsonTextReader(textReader); JObject?jObject = null; try { jObject = JObject.Load(jsonReader, new JsonLoadSettings { LineInfoHandling = LineInfoHandling.Load, CommentHandling = CommentHandling.Load, DuplicatePropertyNameHandling = DuplicatePropertyNameHandling.Ignore, }); } catch (Exception) { } if (jObject is null) { // Just append to existing text return(AppendToEndOfJson(Stringify(PropertyPathToObject(propertyPaths, valueIfNotExist)))); } JObject? currentObject = jObject; List <string> remainingPaths = new(propertyPaths); while (remainingPaths.Count > 0) { string path = PopFromLeft(remainingPaths); JToken?nextLevel = currentObject[path]; if (nextLevel is null) { int line = ((IJsonLineInfo)currentObject).LineNumber - 1; // 1-indexed to 0-indexed int column = ((IJsonLineInfo)currentObject).LinePosition - 1; object insertionValue = valueIfNotExist; remainingPaths.Reverse(); foreach (string propertyName in remainingPaths) { Dictionary <string, object> newObject = new(); newObject[propertyName] = insertionValue; insertionValue = newObject; } remainingPaths.Reverse(); string newPath = string.Join('.', remainingPaths); string insertionValueAsString = Stringify(insertionValue); int insertLine; int insertColumn; int currentIndent; bool hasSiblings = currentObject.Children().Any(child => child.Type != JTokenType.Comment); insertLine = line; insertColumn = column + 1; currentIndent = GetIndentationOfLine(line) + _indent; // use indent of line with the starting "{" as the nested indent level // We will insert before the first sibling string propertyInsertion = "\n" + IndentEachLine( $"\"{path}\": {insertionValueAsString}", currentIndent); if (hasSiblings) { propertyInsertion += ","; } // Need a newline after the insertion if there's anything else on the line var lineStarts = TextCoordinateConverter.GetLineStarts(_json); int offset = TextCoordinateConverter.GetOffset(lineStarts, line, column); char?charAfterInsertion = _json.Length > offset ? _json[offset + 1] : null; if (charAfterInsertion != '\n' && charAfterInsertion != '\r') { propertyInsertion += '\n'; } return(insertLine, insertColumn, propertyInsertion); } if (remainingPaths.Count == 0) { // We found matches all the way to the leaf, doesn't matter what the leaf value is, we will leave it alone return(null); } if (nextLevel is JObject nextObject) { currentObject = nextObject; } else { return(null); } } return(null); }