Select code snippet in document, using the VBA - vba

I want to create a macro, which will allow to select entire code blocks in the document.
This is what I currently have:
Sub SelectSnippet()
Selection.Find.Style = ActiveDocument.Styles("Code")
With Selection.Find
.Text = ""
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute
End Sub
The problem is, that it selects only the next line of code, instead of snippet entirely.
Visually:

This code should do the job. Please try.
Sub SelectSnippet()
Dim Styl As Variant
Dim Rng As Range
Dim Fnd As Boolean
Styl = "Code"
Set Rng = Selection.Range ' start at the selection
' find the nearest different style before the selection
With Rng
Do While .Start > 1
If .Style <> Styl Then Exit Do
.Move wdCharacter, -1
Loop
End With
' look for the first occurrance of the style
On Error Resume Next
With Rng.Find
.Text = ""
.Style = Styl
Fnd = .Execute
End With
If Err Then
MsgBox Err.Description, vbInformation, "Can't find """ & Styl & """"
End If
If Fnd Then
' expand the range to the end of the style
With Rng
Do While .End < .Document.Characters.Count
If .Document.Range(.End, .End + 1).Style <> Styl Then Exit Do
.MoveEnd wdCharacter, 1
Loop
.Select ' select the range
End With
End If
End Sub
The following code does the same job but looks at complete paragraphs only. If a part of a paragraph isn't of the same style it may or may not be included.
Sub NewSelectSnippet()
Dim Styl As Variant
Dim Rng As Range
Dim DocRng As Range
Dim p As Integer
Styl = "Code"
' expand the section to include the entire paragraph
Set Rng = Selection.Paragraphs(1).Range
If Rng.Style <> Styl Then Exit Sub
' expand the range to include preceding paragraphs of same style
Set DocRng = ActiveDocument.Range(0, Rng.End)
With DocRng.Paragraphs
For p = .Count To 1 Step -1
If .Item(p).Range.Style = Styl Then
Rng.MoveStart wdParagraph, -1
Else
Exit For
End If
Next p
End With
' expand the range to include following paragraphs of same style
With ActiveDocument.Paragraphs
For p = (DocRng.Paragraphs.Count + 1) To .Count
If .Item(p).Range.Style = Styl Then
Rng.MoveEnd wdParagraph, 1
Else
Exit For
End If
Next p
End With
Rng.Select
End Sub

Related

How to prevent word from crashing when using batch find and replace macro?

I am using this code which is a batch find and replace macro. It finds and replaces the words in the document by reading the replacement words from another document (text.docx). This works absolutely fine when there are a handful of changes (i.e. less than 1 page). However, I hope to use this macro on documents that are 10-20 pages. When I use it, the word document just immediately crashes (starts not responding) and has to be forced to quit.
Does anyone have any tips on what can be done to prevent it from crashing? How can I modify the code to batch edit thousands of words? Code is below.
Thanks in advance!
Sub ReplaceFromTableList()
Dim oChanges As Document, oDoc As Document
Dim oTable As Table
Dim oRng As Range
Dim rFindText As Range, rReplacement As Range
Dim i As Long
Dim y As Integer
Dim sFname As String
Dim sAsk As String
sFname = "/Users/user/Desktop/test.docx"
Set oDoc = ActiveDocument
Set oChanges = Documents.Open(FileName:=sFname, Visible:=False)
Set oTable = oChanges.Tables(1)
y = 0
For i = 1 To oTable.Rows.Count
Set oRng = oDoc.Range
Set rFindText = oTable.Cell(i, 1).Range
rFindText.End = rFindText.End - 1
Set rReplacement = oTable.Cell(i, 2).Range
rReplacement.End = rReplacement.End - 1
With oRng.Find
.ClearFormatting
.Replacement.ClearFormatting
Do While .Execute(findText:=rFindText, _
MatchWholeWord:=True, _
MatchWildcards:=False, _
Forward:=True, _
Wrap:=wdFindStop) = True
oRng.Select
oRng.FormattedText = rReplacement.FormattedText
y = y + 1
Loop
End With
Next i
oChanges.Close wdDoNotSaveChanges
MsgBox (y & " errors fixed")
End Sub
Your use of the FormattedText method to reproduce the formatting necessitates a time-consuming loop for each expression. The more the find expression occurs in the target document, the longer the process will take. Your unnecessary use of oRng.Select (which you don't then do anything with) makes it even slower - especially since you don't disable ScreenUpdating. The following macro avoids the need for the FormattedText looping:
Sub BulkFindReplace()
Application.ScreenUpdating = False
Dim ThisDoc As Document, FRDoc As Document, Rng As Range, i As Long, j As Long, StrRep As String, StrCount As String
Set ThisDoc = ActiveDocument
Set FRDoc = Documents.Open("C:\Users\" & Environ("Username") & "\Downloads\FindReplaceTable.docx", _
ReadOnly:=True, AddToRecentFiles:=False, Visible:=False)
With ThisDoc.Range.Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindContinue
'Process each word from the F/R Table
For i = 1 To FRDoc.Tables(1).Rows.Count
Set Rng = FRDoc.Tables(1).Rows(i).Cells(1).Range
Rng.End = Rng.End - 1
.Text = Rng
StrCount = StrCount & vbCr & Rng.Text & ":" & vbTab & _
(Len(ThisDoc.Range.Text) - Len(Replace(ThisDoc.Range, Rng.Text, ""))) / Len(Rng.Text)
Set Rng = FRDoc.Tables(1).Rows(i).Cells(2).Range
Rng.End = Rng.End - 1
With Rng
If Len(.Text) > 0 Then
.Copy
StrRep = "^c"
Else
StrRep = ""
End If
End With
.Replacement.Text = StrRep
.Execute Replace:=wdReplaceAll
If i Mod 20 = 0 Then DoEvents
Next
End With
FRDoc.Close False
MsgBox "The following strings were replaced:" & StrCount
Set Rng = Nothing: Set FRDoc = Nothing: Set ThisDoc = Nothing
Application.ScreenUpdating = True
End Sub
Try this:
Sub FindReplaceAll()
Dim MyDialog As FileDialog, GetStr(1 To 100) As String
'100 files is the maximum applying this code
On Error Resume Next
Set MyDialog = Application.FileDialog(msoFileDialogFilePicker)
With MyDialog
.Filters.Clear
.AllowMultiSelect = True
i = 1
If .Show = -1 Then
For Each stiSelectedItem In .SelectedItems
GetStr(i) = stiSelectedItem
i = i + 1
Next
i = i - 1
End If
Application.ScreenUpdating = False
For j = 1 To i Step 1
Set Doc = Documents.Open(FileName:=GetStr(j), Visible:=True)
Windows(GetStr(j)).Activate
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "Marriott International" 'Find What
.Replacement.Text = "Marriott" 'Replace With
.Forward = True
.Wrap = wdFindAsk
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchByte = True
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
Application.Run macroname:="NEWMACROS"
ActiveDocument.Save
ActiveWindow.Close
Next
Application.ScreenUpdating = True
End With
MsgBox "operation end, please view", vbInformation
End Sub
The idea comes from here:
https://www.extendoffice.com/documents/word/1002-word-replace-multiple-files.html

WORD - Find and replace text via Text in quotation marks

How Can I find and trim spaces between quotation text?
for example: if the word contains the following string:
I say to him ' why should I? ' he answers...
It will replace:
I say to him 'why should I?' he answers...
I know that the regular expression to find text in the quotation is:(\'*?\') but from here I could not progress.
Any help will be highly appreciated
Asi
For a VBA solution, try:
Sub Demo()
Dim Rng As Range, Rslt
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "[‘'][!^13^l^t]#['’]"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
.Select
Set Rng = .Duplicate
With Rng
.Start = .Start + 1
.End = .End - 1
If .Text <> Trim(.Text) Then
Rslt = MsgBox("Trim this instance?", vbYesNoCancel)
If Rslt = vbCancel Then Exit Sub
If Rslt = vbYes Then
Do While .Characters.Last = " "
.Characters.Last = vbNullString
Loop
Do While .Characters.First = " "
.Characters.First = vbNullString
Loop
End If
End If
End With
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
End Sub
Note: If the strings don't have any formatting applied, you could reduce:
Do While .Characters.Last = " "
.Characters.Last = vbNullString
Loop
Do While .Characters.First = " "
.Characters.First = vbNullString
Loop
to:
.Text = Trim(.Text)
To work with just a selected range, change:
Dim Rng As Range, Rslt
With ActiveDocument.Range
to:
Dim Rng As Range, RngSel As Range, Rslt
With Selection.Range
Set RngSel = .Duplicate
and insert:
If .InRange(RngSel) = False Then Exit Sub
before:
.Select

move parenthesis to comment in local range in Microsoft Word using VBA

I am trying to move all the text I have in parenthesis to a comment in a range I select. I am almost there but for some reason it only works at the start of the range. My macro is as follows:
Sub CommentOutParenthsLocal()
'
' CommentBubble Macro
'
'
Dim myRange As Range
Set myRange = Selection.Range
searchText = "\(*\)"
With myRange.Find
.MatchWildcards = True
Do While .Execute(findText:=searchText, Forward:=True) = True
ActiveDocument.Comments.Add myRange, myRange.Text
myRange.Text = ""
Loop
End With
End Sub
Any advice?
Based on your description, you need to limit your code's scope to what you've actually selected, amongst other things. In that case, try:
Sub CommentOutParenthsLocal()
Application.ScreenUpdating = True
Dim myRange As Range
Set myRange = Selection.Range
With Selection.Range
With .Find
.Text = "\(*\)"
.Forward = True
.MatchWildcards = True
.Execute
End With
Do While .Find.Found = True
If .InRange(myRange) = False Then Exit Do
.Comments.Add .Duplicate, .Text
.Text = vbNullString
.Find.Execute
Loop
End With
Application.ScreenUpdating = False
End Sub

Searching for words in word, but ignoring tables

I have the fantastic macro below which
Searches for words (listed in an excel file)
Copies each instance
Pastes into a new word document together with it's location from the original document
This has been created and amended by various people and I am truly greatful!!. One thing that I was wondering if possible is:
If in the word document which you're searching there are tables, can you make the macro to ignore tables? or would it be better to say 'If the word is found and is in a table ignore this instance and proceed searching te document again'
The latter would have more unnecessary iterations in my opinion.
I had managed to find the code:
Sub NonTableParagraphs()
Dim rng() As Range
Dim t As Integer
Dim tbl As Table
Dim para As Paragraph
Dim r As Integer
ReDim Preserve rng(t)
Set rng(t) = ActiveDocument.Range
For Each tbl In ActiveDocument.Tables
rng(t).End = tbl.Range.Start
t = t + 1
ReDim Preserve rng(t)
Set rng(t) = ActiveDocument.Range
rng(t).Start = tbl.Range.End
Next tbl
rng(t).End = ActiveDocument.Range.End
For r = 0 To t
For Each para In rng(r).Paragraphs
'do processing
Next para
Next r
End Sub
and had tried to insert NonTableParagraphs in the original macro, so it would run a sub routine, but I couldn't get it to work.
It looks like I should be trying to use ActiveDocument.Tables and somehow stating if ActiveDocument.Tables found, skip the rest of the lines in macro & then return to searching after the table but I can't seem to get it to work.
I'll see if I can search for that
Many thanks!!!
Sub CopyKeywordPlusContext()
'Modified 3-10-2015 TW
'Modified 2-17-2015 GKM
'Makro created on 22.01.2013
Dim oDoc As Document, oDocRecord As Document
Dim strSearch As String, arrSearch
Dim lngCharTrailing As Long, lngCharLeading As Long, lngIndex As Long, lngCount As Long
Dim lngPgNum, lngLineNum As Integer
Dim oRng As Word.Range, oRngSpan As Word.Range
Dim bFound As Boolean
Dim oTbl As Word.Table
strSearch = vbNullString
Dim xl As Object
Set xl = GetObject(, "Excel.Application")
arrSearch = xl.transpose(xl.activesheet.Range("A1:A221").Value)
lngCharLeading = 20
lngCharTrailing = 20
Set oDoc = ActiveDocument
For lngIndex = 1 To UBound(arrSearch)
ResetFRParams
bFound = False
lngCount = 0
Set oRng = oDoc.Range
With oRng.Find
.Text = LCase(arrSearch(lngIndex))
While .Execute
bFound = True
If oDocRecord Is Nothing Then
Set oDocRecord = Documents.Add
Set oTbl = oDocRecord.Tables.Add(oDocRecord.Range, 1, 2)
End If
lngCount = lngCount + 1
If lngCount = 1 Then
oTbl.Rows.Add
With oTbl.Rows.Last.Previous
.Cells.Merge
With .Cells(1).Range
.Text = "Search results for """ & arrSearch(lngIndex) & """ + context in " & """" & oDoc.Name & """"
.Font.Bold = True
End With
End With
End If
Set oRngSpan = oRng.Duplicate
oRngSpan.Select
lngPgNum = Selection.Information(wdActiveEndPageNumber)
lngLineNum = Selection.Information(wdFirstCharacterLineNumber)
With oRngSpan
.MoveStart wdCharacter, -lngCharLeading
.MoveEnd wdCharacter, lngCharTrailing
Do While oRngSpan.Characters.First = vbCr
oRngSpan.MoveStart wdCharacter, -1
Loop
Do While oRngSpan.Characters.Last = vbCr
oRngSpan.MoveEnd wdCharacter, 1
If oRngSpan.End = oDoc.Range.End Then
oRngSpan.End = oRngSpan.End - 1
Exit Do
End If
Loop
End With
oTbl.Rows.Last.Range.Cells(1).Range.Text = Trim(oRngSpan.Text)
oTbl.Rows.Last.Range.Cells(2).Range.Text = "Page: " & lngPgNum & " Line: " & lngLineNum
oTbl.Rows.Add
Wend
End With
If bFound Then
ResetFRParams
With oDocRecord.Range.Find
.Text = LCase(arrSearch(lngIndex))
.Replacement.Text = "^&"
.Replacement.Highlight = True
.Format = True
.Execute Replace:=wdReplaceAll
End With
End If
Next lngIndex
oTbl.Rows.Last.Delete
End Sub
Sub ResetFRParams()
With Selection.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Replacement.Highlight = False
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
lbl_Exit:
Exit Sub
End Sub
Instead of trying to debug/edit your code look at this and decide for yourself where to insert it.
Sub FindText()
Dim doc As Word.Document, rng As Word.Range
Set doc = Word.ActiveDocument
Set rng = doc.Content
With rng.Find
.ClearFormatting
.Format = False
.Forward = True
.Text = "Now is"
.Wrap = wdFindStop
.Execute
Do While .Found
If rng.Information(Word.WdInformation.wdWithInTable) Then
'do nothing
rng.Collapse Word.WdCollapseDirection.wdCollapseEnd
Else
rng.Text = "Now is not"
rng.Collapse Word.WdCollapseDirection.wdCollapseEnd
End If
.Execute
Loop
End With
End Sub

VBA - Get the range as Selection

I'm using Range.Find to find a specific string in a document. When I find this string I want to look at the character BEFORE this string. I had an idea to get the range as selection and then use the Selection.MoveLeft = 1 but I really can't find how to get the range as selection. This is the code I have:
Private Function abc() As Boolean
Set rng = ActiveDocument.Range
With rng.Find
.MatchWildcards = True
Do While .Execute(findText:="123456", Forward:=False) = True
MsgBox (rng.Text)
Set Selection = rng 'Set the selection from range
MsgBox (Selection.Text)
Selection.MoveLeft = 1 'Move the selection
MsgBox (Selection.Text)
Loop
End With
abc = True
End Function
Solution
Here is my solution.
Sub testhis()
Set rng = ActiveDocument.Range
With rng.Find
.MatchWildcards = True
Do While .Execute(findText:="123456", Forward:=False) = True
rng.Select
Selection.MoveLeft Unit:=wdCharacter, Count:=2
MsgBox (Selection.Text)
Loop
End With
End Sub
Hope this helps.
Here's a way you can do it without Selecting
Sub abc()
Dim rng As Range
Set rng = ActiveDocument.Range
With rng.Find
.MatchWildcards = True
Do While .Execute(findText:="123456", Forward:=False) = True
MsgBox rng.Text
rng.Move wdCharacter, -2
rng.Expand wdCharacter
MsgBox rng.Text
Loop
End With
End Sub