protected override void Undo( BlockCommandContext context, Block block) { // Revert the block type. block.SetBlockType(previousBlockType); }
public static void GetCounts( IProjectPlugin project, Block block, out int count, out int wordCount, out int characterCount, out int nonWhitespaceCount) { // Make sure we have a sane state. if (project == null) { throw new ArgumentNullException("project"); } if (block == null) { throw new ArgumentNullException("block"); } // Figure out the root path for the various components. HierarchicalPath rootPath = GetPluginRootPath(project); count = GetCount(block, rootPath, "Total/" + CountType); wordCount = GetCount(block, rootPath, "Total/" + WordCountType); characterCount = GetCount(block, rootPath, "Total/" + CharacterCountType); nonWhitespaceCount = GetCount( block, rootPath, "Total/" + NonWhitespaceCountType); }
protected override void Do( BlockCommandContext context, Block block) { // Save the previous text so we can restore it. previousText = block.Text; originalPosition = context.Position; // Figure out what the new text string would be. startIndex = BlockPosition.TextIndex.GetCharacterIndex( block.Text, End, WordSearchDirection.Left); int endIndex = End.GetCharacterIndex( block.Text, TextIndex, WordSearchDirection.Right); int firstIndex = Math.Min(startIndex, endIndex); int lastIndex = Math.Max(startIndex, endIndex); string newText = block.Text.Remove(firstIndex, lastIndex - firstIndex); // Set the new text into the block. This will fire various events to // trigger the immediate and background processing. block.SetText(newText); // Set the position after the next text. if (UpdateTextPosition.HasFlag(DoTypes.Do)) { context.Position = new BlockPosition(BlockKey, startIndex); } }
/// <summary> /// Performs the command on the given block. /// </summary> /// <param name="context"></param> /// <param name="block">The block to perform the action on.</param> /// <param name="project">The project that contains the current state.</param> protected override void Do( BlockCommandContext context, Block block) { // Save the previous text so we can undo it. previousText = block.Text; // Figure out what the new text string would be. int textIndex = new CharacterPosition(TextIndex).GetCharacterIndex( block.Text); string newText = block.Text.Insert(textIndex, Text); // Set the new text into the block. This will fire various events to // trigger the immediate and background processing. block.SetText(newText); // After we insert text, we need to give the immediate editor plugins a // chance to made any alterations to the output. block.Project.Plugins.ProcessImmediateEdits( context, block, textIndex + Text.Length); // Set the new position in the buffer. if (UpdateTextPosition.HasFlag(DoTypes.Do)) { context.Position = new BlockPosition(BlockKey, textIndex + Text.Length); } }
protected override void Do( BlockCommandContext context, Block block) { // Pull out some common elements we'll need. ProjectBlockCollection blocks = block.Blocks; int blockIndex = blocks.IndexOf(block) + 1; // Because of how block keys work, the ID is unique very time so we have // to update our inverse operation. addedBlocks.Clear(); // Go through and create each block at a time, adding it to the inverse // command as we create them. for (int count = 0; count < Count; count++) { // Create and insert a new block into the system. var newBlock = new Block(blocks); blocks.Insert(blockIndex, newBlock); // Keep track of the block so we can remove them later. addedBlocks.Add(newBlock); // Update the position. if (UpdateTextPosition.HasFlag(DoTypes.Do)) { context.Position = new BlockPosition(newBlock.BlockKey, 0); } } }
public InsertIndexedBlockCommand( int blockIndex, Block block) { BlockIndex = blockIndex; Block = block; UpdateTextPosition = DoTypes.All; }
protected override void Undo( BlockCommandContext context, Block block) { block.SetText(previousText); if (UpdateTextPosition.HasFlag(DoTypes.Undo)) { context.Position = new BlockPosition(BlockKey, previousText.Length); } }
public BlockAnalyzer( Block block, int blockVersion, IList<IBlockAnalyzerProjectPlugin> blockAnalyzers, HashSet<IBlockAnalyzerProjectPlugin> analysis) { Block = block; BlockVersion = blockVersion; BlockAnalyzers = blockAnalyzers; Analysis = analysis; }
protected override void Do( BlockCommandContext context, Block block) { previousText = block.Text; block.SetText(Text); if (UpdateTextPosition.HasFlag(DoTypes.Do)) { context.Position = new BlockPosition(BlockKey, Text.Length); } }
public void Do(BlockCommandContext context) { using (context.Blocks.AcquireLock(RequestLock.Write)) { // We need the index of the block so we can restore it back into // its place. Block block = context.Blocks[blockKey]; removedBlockIndex = context.Blocks.IndexOf(blockKey); removedBlock = block; // Delete the block from the list. context.Blocks.Remove(block); // If we have no more blocks, then we need to ensure we have a minimum // number of blocks. addedBlankBlock = null; if (!IgnoreMinimumLines && context.Blocks.Count == 0) { // Create a new placeholder block, which is blank. addedBlankBlock = new Block( context.Blocks, block.Project.BlockTypes.Paragraph); context.Blocks.Add(addedBlankBlock); if (UpdateTextPosition.HasFlag(DoTypes.Do)) { context.Position = new BlockPosition(addedBlankBlock.BlockKey, 0); } } else if (!IgnoreMinimumLines) { // We have to figure out where the cursor would be after this operation. // Ideally, this would be the block in the current position, but if this // is the last line, then use that. if (UpdateTextPosition.HasFlag(DoTypes.Do)) { context.Position = new BlockPosition( removedBlockIndex < context.Blocks.Count ? context.Blocks[removedBlockIndex].BlockKey : context.Blocks[removedBlockIndex - 1].BlockKey, 0); } } } }
protected override void Do( BlockCommandContext context, Block block) { // We need to keep track of the previous block type so we can change // it back with Undo. previousBlockType = block.BlockType; // Set the block type. block.SetBlockType(BlockType); // Save the position from this command. if (UpdateTextPosition.HasFlag(DoTypes.Do)) { context.Position = new BlockPosition(BlockKey, 0); } }
public void ProcessImmediateEdits( BlockCommandContext context, Block block, int textIndex) { // Get the plugin settings from the project. ImmediateBlockTypesSettings settings = Settings; // Grab the substring from the beginning to the index and compare that // in the dictionary. string text = block.Text.Substring(0, textIndex); if (!settings.Replacements.ContainsKey(text)) { // We want to fail as fast as possible. return; } // If the block type is already set to the same name, skip it. string blockTypeName = settings.Replacements[text]; BlockType blockType = Project.BlockTypes[blockTypeName]; if (block.BlockType == blockType) { return; } // Perform the substitution with a replace operation and a block change // operation. var replaceCommand = new ReplaceTextCommand( new BlockPosition(block.BlockKey, 0), textIndex, string.Empty); var changeCommand = new ChangeBlockTypeCommand(block.BlockKey, blockType); // Create a composite command that binds everything together. var compositeCommand = new CompositeCommand<BlockCommandContext>(true, false); compositeCommand.Commands.Add(replaceCommand); compositeCommand.Commands.Add(changeCommand); // Add the command to the deferred execution so the command could // be properly handled via the undo/redo management. block.Project.Commands.DeferDo(compositeCommand); }
/// <summary> /// Gets the deltas as a dictionary of key and deltas for the block. /// </summary> /// <param name="project">The project.</param> /// <param name="block">The block.</param> /// <param name="wordDelta">The word delta.</param> /// <param name="characterDelta">The character delta.</param> /// <param name="nonWhitespaceDelta">The non whitespace delta.</param> /// <returns> /// A dictionary of paths and deltas. /// </returns> public static Dictionary<HierarchicalPath, int> GetDeltas( IProjectPlugin project, Block block, int delta, int wordDelta, int characterDelta, int nonWhitespaceDelta) { // Make sure we have a sane arguments. if (project == null) { throw new ArgumentNullException("project"); } if (block == null) { throw new ArgumentNullException("block"); } // Create the dictionary and figure out the top-level elements. var deltas = new Dictionary<HierarchicalPath, int>(); HierarchicalPath rootPath = GetPluginRootPath(project); // Add in the path for the totals. var totalPath = new HierarchicalPath("Total", rootPath); AddDeltas( deltas, totalPath, delta, wordDelta, characterDelta, nonWhitespaceDelta); // Add in a block-type specific path along with a counter. string relativeBlockPath = "Block Types/" + block.BlockType.Name; var blockPath = new HierarchicalPath(relativeBlockPath, rootPath); AddDeltas( deltas, blockPath, delta, wordDelta, characterDelta, nonWhitespaceDelta); // Return the resulting delta. return deltas; }
/// <summary> /// Raises an event that a block's text spans had changed. /// </summary> /// <param name="block"></param> public void RaiseTextSpansChanged(Block block) { EventHandler<BlockEventArgs> listeners = TextSpansChanged; if (listeners != null) { var args = new BlockEventArgs(block); listeners(this, args); } }
/// <summary> /// Ensures the minimum blocks inside the collection. /// </summary> private void EnsureMinimumBlocks() { if (Count == 0) { var initialBlock = new Block(this); Add(initialBlock); } }
public void ProcessImmediateEdits( BlockCommandContext context, Block block, int textIndex) { // If we aren't optimized, we have to pull the settings back in from the // project settings and optimize them. if (!optimizedSubstitions) { RetrieveSettings(); } // Pull out the edit text and add a leading space to simplify the // "whole word" substitutions. string editText = block.Text.Substring(0, textIndex); if (editText.Length - 1 < 0) { return; } // Figure out if we're at a word break. char finalCharacter = editText[editText.Length - 1]; bool isWordBreak = char.IsPunctuation(finalCharacter) || char.IsWhiteSpace(finalCharacter); // Go through the substitution elements and look for each one. foreach (RegisteredSubstitution substitution in Substitutions) { // If we are doing whole word searches, then we don't bother if // the final character isn't a word break or if it isn't a word // break before it. ReplaceTextCommand command; int searchLength = substitution.Search.Length; int startSearchIndex = editText.Length - searchLength; // If we are going to be searching before the string, then this // search term will never be valid. if (startSearchIndex < 0) { continue; } // Do the search based on the whole word or suffix search. if (substitution.IsWholeWord) { // Check to see if we have a valid search term. if (!isWordBreak) { continue; } if (startSearchIndex > 0 && char.IsPunctuation(editText[startSearchIndex - 1])) { continue; } if (startSearchIndex - 1 < 0) { continue; } // Make sure the string we're looking at actually is the same. string editSubstring = editText.Substring( startSearchIndex - 1, substitution.Search.Length); if (editSubstring != substitution.Search) { // The words don't match. continue; } // Perform the substitution with a replace operation. command = new ReplaceTextCommand( new BlockPosition(block.BlockKey, startSearchIndex - 1), searchLength + 1, substitution.Replacement + finalCharacter); } else { // Perform a straight comparison search. if (!editText.EndsWith(substitution.Search)) { continue; } // Figure out the replace operation. command = new ReplaceTextCommand( new BlockPosition(block.BlockKey, startSearchIndex), searchLength, substitution.Replacement); } // Add the command to the deferred execution so the command could // be properly handled via the undo/redo management. block.Project.Commands.DeferDo(command); } }
public void AnalyzeBlock( Block block, int blockVersion) { // Grab the information about the block. string text; var originalMispelledWords = new TextSpanCollection(); using (block.AcquireBlockLock(RequestLock.Read)) { // If we are stale, then break out. if (block.IsStale(blockVersion)) { return; } // Grab the information from the block. We need the text and // alow the current spelling areas. text = block.Text; originalMispelledWords.AddRange( block.TextSpans.Where(span => span.Controller == this)); } // Split the word and perform spell-checking. var misspelledWords = new List<TextSpan>(); IList<TextSpan> words = Splitter.SplitAndNormalize(text); IEnumerable<TextSpan> misspelledSpans = words.Where(span => !IsCorrect(span.GetText(text))); foreach (TextSpan span in misspelledSpans) { // We aren't correct, so add it to the list. span.Controller = this; misspelledWords.Add(span); } // Look to see if we have any change from the original spelling // errors and this one. This will only happen if the count is // identical and every one in the original list is in the new list. if (originalMispelledWords.Count == misspelledWords.Count) { bool isMatch = originalMispelledWords.All(misspelledWords.Contains); if (isMatch) { // There are no new changes, so we don't have anything to // update. return; } } // Inside a write lock, we need to make modifications to the block's list. using (block.AcquireBlockLock(RequestLock.Write)) { // Check one last time to see if the block is stale. if (block.IsStale(blockVersion)) { return; } // Make the changes to the block's contents. block.TextSpans.Remove(this); block.TextSpans.AddRange(misspelledWords); // Raise that we changed the spelling on the block. block.RaiseTextSpansChanged(); } }
/// <summary> /// Gets the editor actions associated with the given TextSpan. /// </summary> /// <param name="block">The block.</param> /// <param name="textSpan">The text span.</param> /// <returns> /// A list of editor actions associated with this span. /// </returns> /// <remarks> /// This will be called within a read-only lock. /// </remarks> public IList<IEditorAction> GetEditorActions( Block block, TextSpan textSpan) { // We only get to this point if we have a misspelled word. string word = textSpan.GetText(block.Text); // Get the suggestions for the word. IList<SpellingSuggestion> suggestions = GetSuggestions(word); // Go through the suggestions and create an editor action for each one. // These will already be ordered coming out of the GetSuggestions() // method. BlockCommandSupervisor commands = block.Project.Commands; var actions = new List<IEditorAction>(suggestions.Count); foreach (SpellingSuggestion suggestion in suggestions) { // Figure out the operation we'll be using to implement the change. var command = new ReplaceTextCommand( new BlockPosition(block.BlockKey, textSpan.StartTextIndex), textSpan.Length, suggestion.Suggestion); // Create the suggestion action, along with the replacement command. var action = new EditorAction( string.Format("Change to \"{0}\"", suggestion.Suggestion), new HierarchicalPath("/Plugins/Spelling/Change"), context => commands.Do(command, context)); actions.Add(action); } // Add the additional editor actions from the plugins. foreach (ISpellingProjectPlugin controller in SpellingControllers) { IEnumerable<IEditorAction> additionalActions = controller.GetAdditionalEditorActions(word); actions.AddRange(additionalActions); } // Return all the change actions. return actions; }
/// <summary> /// Analyzes the block and counts the word. Once counted, this updates /// the block and all parent blocks with the altered change. /// </summary> /// <param name="block">The block.</param> /// <param name="blockVersion">The block version of the initial request.</param> public void AnalyzeBlock( Block block, int blockVersion) { // Grab counts from the current block text. int newCount = 1; int newWordCount; int newCharacterCount; int newNonWhitespaceCount; string text = block.Text; WordCounter.CountWords( text, out newWordCount, out newCharacterCount, out newNonWhitespaceCount); // Grab the existing counts from the current block, if we have one. int oldCount; int oldWordCount; int oldCharacterCount; int oldNonWhitespaceCount; WordCounterPathUtility.GetCounts( this, block, out oldCount, out oldWordCount, out oldCharacterCount, out oldNonWhitespaceCount); // Calculate the deltas between the values. int delta = newCount - oldCount; int wordDelta = newWordCount - oldWordCount; int characterDelta = newCharacterCount - oldCharacterCount; int nonWhitespaceDelta = newNonWhitespaceCount - oldNonWhitespaceCount; // Build up a dictionary of changes so we can have a simple loop to // set them in the various elements. Dictionary<HierarchicalPath, int> deltas = WordCounterPathUtility.GetDeltas( this, block, delta, wordDelta, characterDelta, nonWhitespaceDelta); // Get a write lock on the blocks list and update that block and all // parent blocks in the document. using (block.AcquireBlockLock(RequestLock.Write)) { // Log that we are analyzing this block. Log("BEGIN AnalyzeBlock: {0}: Words {1:N0}", block, newWordCount); // First check to see if we've gotten stale. if (block.IsStale(blockVersion)) { return; } // Update the block and the document. UpdateDeltas(block, deltas); UpdateDeltas(block.Project, deltas); // Log that we finished processing this block. Log("END AnalyzeBlock: {0}: Words {1:N0}", block, newWordCount); } }
/// <summary> /// Inserts the lines. /// </summary> /// <param name="project">The project.</param> /// <param name="lineCount">The line count.</param> private void InsertLines( Project project, int lineCount) { // Pull out some useful variables. ProjectBlockCollection blocks = project.Blocks; // Modify the first line, which is always there. using (blocks[0].AcquireBlockLock(RequestLock.Write)) { blocks[0].SetText("Line 1"); } // Add in the additional lines after the first one. for (int i = 1; i < lineCount; i++) { var block = new Block(blocks); using (block.AcquireBlockLock(RequestLock.Write)) { block.SetText("Line " + (i + 1)); } blocks.Add(block); } }
/// <summary> /// Gets the editor actions for a given text span. /// </summary> /// <param name="block">The block.</param> /// <param name="textSpan">The text span.</param> /// <returns></returns> public IList<IEditorAction> GetEditorActions( Block block, TextSpan textSpan) { // Loop through all the text span controllers and add their actions to // the list. IEnumerable<ProjectPluginController> controllers = Controllers.Where( controller => controller.ProjectPlugin is ITextControllerProjectPlugin); var actions = new List<IEditorAction>(); foreach (ProjectPluginController controller in controllers) { var textSpanController = (ITextControllerProjectPlugin) controller.ProjectPlugin; IList<IEditorAction> controllerActions = textSpanController.GetEditorActions(block, textSpan); actions.AddRange(controllerActions); } // Return the resulting list of actions. return actions; }
/// <summary> /// Acquires a lock on the collection, of the requested type, and also a lock /// on the block referenced by the index. /// </summary> /// <param name="blockIndex">The index of the block to lock.</param> /// <param name="block">The block retrieved by the index.</param> /// <param name="requestedBlockLock"></param> /// <returns>An opaque lock object that will release the lock on disposal.</returns> public IDisposable AcquireBlockLock( RequestLock requestedBlockLock, int blockIndex, out Block block) { return AcquireBlockLock( RequestLock.Read, requestedBlockLock, blockIndex, out block); }
public void Do(BlockCommandContext context) { // We have to clear the undo buffer every time because we'll be creating // new blocks. addedBlocks.Clear(); // Start by breaking apart the lines on the newline. string[] lines = Text.Split('\n'); // Make changes to the first line by creating a command, adding it to the // list of commands we need an inverse for, and then performing it. Block block = context.Blocks[BlockPosition.BlockKey]; string remainingText = block.Text.Substring((int) BlockPosition.TextIndex); deleteFirstCommand = new DeleteTextCommand(BlockPosition, block.Text.Length); insertFirstCommand = new InsertTextCommand(BlockPosition, lines[0]); deleteFirstCommand.Do(context); insertFirstCommand.Do(context); // Update the final lines text with the remains of the first line. int lastLineLength = lines[lines.Length - 1].Length; lines[lines.Length - 1] += remainingText; // For the remaining lines, we need to insert each one in turn. if (UpdateTextPosition.HasFlag(DoTypes.Do)) { context.Position = BlockPosition.Empty; } if (lines.Length > 1) { // Go through all the lines in reverse order to insert them. int firstBlockIndex = context.Blocks.IndexOf(block); for (int i = lines.Length - 1; i > 0; i--) { // Insert the line and set its text value. var newBlock = new Block(context.Blocks); addedBlocks.Add(newBlock); using (newBlock.AcquireBlockLock(RequestLock.Write)) { newBlock.SetText(lines[i]); } context.Blocks.Insert(firstBlockIndex + 1, newBlock); // Update the last position as we go. if (context.Position == BlockPosition.Empty) { if (UpdateTextPosition.HasFlag(DoTypes.Do)) { context.Position = new BlockPosition( newBlock.BlockKey, (CharacterPosition) lastLineLength); } } } } }
public void ChangeBlockType( Block block, BlockType oldBlockType) { // We need a write lock on the blocks while we make this change. using (block.AcquireBlockLock(RequestLock.Write)) { // Report what we're doing if we have logging on. Log("ChangeBlockType: {0}: Old Type {1}", block, oldBlockType); //// Figure out the deltas for this block. //var deltas = new Dictionary<HierarchicalPath, int>(); //deltas[WordCounterPathUtility.GetPath(oldBlockType)] = -1; //deltas[WordCounterPathUtility.GetPath(block.BlockType)] = 1; //// Update the parent types. //UpdateDeltas(block, deltas); //UpdateDeltas(block.Project, deltas); } }
public void Undo(BlockCommandContext context) { using (context.Blocks.AcquireLock(RequestLock.Write)) { // Insert in the old block. context.Blocks.Insert(removedBlockIndex, removedBlock); // Set the last text position. if (UpdateTextPosition.HasFlag(DoTypes.Undo)) { context.Position = new BlockPosition(blockKey, removedBlock.Text.Length); } // Remove the blank block, if we added one. if (addedBlankBlock != null) { context.Blocks.Remove(addedBlankBlock); addedBlankBlock = null; } } }
protected override void Undo( BlockCommandContext context, Block block) { foreach (Block addedBlock in addedBlocks) { context.Blocks.Remove(addedBlock); } if (UpdateTextPosition.HasFlag(DoTypes.Undo)) { context.Position = new BlockPosition(BlockKey, block.Text.Length); } }
/// <summary> /// Processes any block analysis on the given block. /// </summary> /// <param name="block">The block.</param> public async void ProcessBlockAnalysis(Block block) { // If we don't have any analysis controllers, we don't have to do anything. if (BlockAnalyzers.Count == 0) { return; } // Keep track of the running tasks so we can wait for them to finish // (if needed). Interlocked.Increment(ref tasksRunning); try { // Grab information about the block inside a read lock. int blockVersion; HashSet<IBlockAnalyzerProjectPlugin> analysis; using (block.AcquireBlockLock(RequestLock.Read)) { blockVersion = block.Version; analysis = block.GetAnalysis(); } // Create a background task that will analyze the block. This will return // false if the block had changed in the process of analysis (which would // have triggered another background task). var analyzer = new BlockAnalyzer( block, blockVersion, BlockAnalyzers, analysis); Task task = Task.Factory.StartNew(analyzer.Run); // Wait for the task to complete in the background so we can then // decrement our running counter. await task; } finally { // Decrement the counter to keep track of running tasks. Interlocked.Decrement(ref tasksRunning); } }
/// <summary> /// Checks for immediate edits on a block. This is intended to be a blocking /// editing that will always happen within a write lock. /// </summary> /// <param name="context">The context of the edit just performed.</param> /// <param name="block">The block associated with the current changes.</param> /// <param name="textIndex">Index of the text inside the block.</param> public void ProcessImmediateEdits( BlockCommandContext context, Block block, int textIndex) { foreach ( IImmediateEditorProjectPlugin immediateBlockEditor in ImmediateEditors) { immediateBlockEditor.ProcessImmediateEdits(context, block, textIndex); } }
public override LineBufferOperationResults InsertLines( int lineIndex, int count) { var composite = new CompositeCommand<BlockCommandContext>(true, false); using (project.Blocks.AcquireLock(RequestLock.Write)) { for (int i = 0; i < count; i++) { var block = new Block(project.Blocks); var command = new InsertIndexedBlockCommand(lineIndex, block); composite.Commands.Add(command); } var context = new BlockCommandContext(project); project.Commands.Do(composite, context); return GetOperationResults(); } }
protected abstract void Undo( BlockCommandContext context, Block block);