Community
    • Login

    Is there a SCI_SETLINE type message (How to update a line)

    Scheduled Pinned Locked Moved Notepad++ & Plugin Development
    19 Posts 6 Posters 1.2k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Alan KilbornA
      Alan Kilborn @Paul Baker
      last edited by Alan Kilborn

      @Paul-Baker

      I believe what you want to do is:

      • get the starting position of the line of interest
      • get the ending position of the line of interest (in all likelihood you want the position before the line ending characters)
      • delete the text between the two positions
      • insert your new text at the starting position

      In other words, there isn’t (I don’t believe) a function for directly doing it in one step.

      But, you could make such a C++ function, and publish it here, and then future plugin developers could use it and thus benefit.

      EDIT after reading @Coises 's response below: I didn’t mention “replace target” as an option, because apparently OP knows about that (it’s in his posting).

      1 Reply Last reply Reply Quote 1
      • CoisesC
        Coises @Paul Baker
        last edited by

        @Paul-Baker

        You can use SCI_POSITIONFROMLINE and SCI_GETLINEENDPOSITION to get the character positions that correspond to a given line number. (No need to use SCI_GOTOLINE.) Then use SCI_SETTARGETRANGE to set the target range, SCI_GETTARGETTEXT to retrieve the current contents, and SCI_REPLACETARGET to change it.

        If you will never add line-ending characters, you can just iterate through the selected line numbers. If the replacements could include line ending characters, you will have to be careful.

        1 Reply Last reply Reply Quote 2
        • PeterJonesP
          PeterJones @Paul Baker
          last edited by PeterJones

          @Paul-Baker ,

          The PythonScript plugin made a wrapper/helper function called replaceLine() : you can find the source code for that function here – it is essentially following the suggestions that @Coises made …

          And I had been thinking along @Alan-Kilborn’s lines, with doing the SCI_DELETERANGE then doing a SCI_INSERTTEXT at the old start-position.

          edited phrasing once I saw the subtle difference between Alan’s and Coises’ suggestions

          Alan KilbornA 1 Reply Last reply Reply Quote 2
          • Alan KilbornA
            Alan Kilborn @PeterJones
            last edited by

            @PeterJones said in Is there a SCI_SETLINE type message (How to update a line):

            following the suggestions that both @Alan-Kilborn and @Coises made nearly simultaneously.

            Perhaps ALL THREE of us need to go AFK and get out and enjoy the weekend! :-)

            PeterJonesP 1 Reply Last reply Reply Quote 1
            • PeterJonesP
              PeterJones @Alan Kilborn
              last edited by PeterJones

              @Alan-Kilborn said in Is there a SCI_SETLINE type message (How to update a line):

              Perhaps ALL THREE of us need to go AFK and get out and enjoy the weekend! :-)

              I already got in my walk this morning, between the downpours.

              /me listens to the intense rainfall.

              Right now would be a really bad time for going out. :-)

              1 Reply Last reply Reply Quote 2
              • Paul BakerP
                Paul Baker
                last edited by

                Thanks everyone for the input… I’m glad you’re not AFK. :) It’s a crappy day here in ATL so. I’ll give your suggestions a try.

                Thank you!

                1 Reply Last reply Reply Quote 1
                • Paul BakerP
                  Paul Baker
                  last edited by

                  Solved!
                  This is what I came up with. I’m not a c++ guy so please don’t ding me for the code. Thank you for your suggestions. That really helped.

                  Use Case:
                  Provide capability that allows the user to select one or more lines to be reformatted per specs. The plugin will get the text for each line, manipulate it, and update in Scintilla.

                  If you see any errors, please let me know.

                  void UpdateText() {
                  
                  	::MessageBox(nppData._nppHandle, TEXT("Comment Plugin"), TEXT("The Comment Plugin"), MB_OK);
                  
                  	// Get handle to Scintilla editor
                  	int currentEdit;
                  	::SendMessage(nppData._nppHandle, NPPM_GETCURRENTSCINTILLA, 0, (LPARAM)&currentEdit);
                  	HWND curScint = (currentEdit == 0) ? nppData._scintillaMainHandle : nppData._scintillaSecondHandle;
                  
                  	// Get selection start and end positions.
                  	// If selectionStr and selectionEnd are equal then there is no selection,
                  	// just process the line the caret is on.   
                  	int strSel = (int)::SendMessage(curScint, SCI_GETSELECTIONSTART, 0, 0);
                  	int endSel = (int)::SendMessage(curScint, SCI_GETSELECTIONEND, 0, 0);
                  	
                  	// Get the line numbers associated with the start and end positions.
                  	// The strLine and endLine may be the same line.
                  	int strLine = (int)::SendMessage(curScint,SCI_LINEFROMPOSITION, strSel,0);
                  	int endLine = (int)::SendMessage(curScint, SCI_LINEFROMPOSITION, endSel, 0);
                  
                  	size_t lineLen;
                  	int strPos;
                  	int endPos;
                  	char* selLine = 0;
                  
                  	for (int lineNum = strLine; lineNum <= endLine; lineNum++) {
                  
                  		// Calculate line size by getting the line start and end positions
                  		strPos = (int) ::SendMessage(curScint, SCI_POSITIONFROMLINE, lineNum, 0);
                  		endPos = (int) ::SendMessage(curScint, SCI_GETLINEENDPOSITION, lineNum, 0);
                  
                  		// Get the current line. 
                  		lineLen = endPos - strPos + 1;
                  		selLine = new char[lineLen];
                  		::SendMessage(curScint, SCI_SETTARGETRANGE, strPos, endPos);
                  		::SendMessage(curScint, SCI_GETTARGETTEXT, 0, reinterpret_cast<LPARAM>(selLine));
                  
                  		// Move char* to string for easier (for me) string handling
                  		// First make sure line text is 
                  		selLine[lineLen - 1] = '\0';
                  		std::string lineText = selLine;
                  
                  		// Do something.
                  
                  		selLine = new char[lineText.length()];
                  		strcpy(selLine, lineText.c_str());
                  		::SendMessage(curScint, SCI_REPLACETARGET, static_cast<WPARAM>(-1), reinterpret_cast<LPARAM>(selLine));
                  
                  	}
                  
                  	return;
                  }
                  
                  CoisesC Alan KilbornA Paul BakerP 3 Replies Last reply Reply Quote 1
                  • CoisesC
                    Coises @Paul Baker
                    last edited by

                    @Paul-Baker said in Is there a SCI_SETLINE type message (How to update a line):

                    If you see any errors, please let me know.

                    You are leaking memory; you allocate new C-style strings, but you never delete them.

                    Recommendation: Save yourself some grief and use the fact that as of C++17 you can modify the data of a std::string. (You can even overwrite the trailing null that comes one after the length, so long as you overwrite it with another null.)

                    // Get the current line.
                    lineLen = endPos - strPos;
                    std::string lineText(lineLen, 0);
                    ::SendMessage(curScint, SCI_SETTARGETRANGE, strPos, endPos);
                    ::SendMessage(curScint, SCI_GETTARGETTEXT, 0, reinterpret_cast<LPARAM>(lineText.data()));
                    
                    // Do something.
                    
                    ::SendMessage(curScint, SCI_REPLACETARGET, static_cast<WPARAM>(-1), reinterpret_cast<LPARAM>(lineText.data()));
                    
                    Paul BakerP 1 Reply Last reply Reply Quote 2
                    • Paul BakerP
                      Paul Baker @Coises
                      last edited by Paul Baker

                      @Coises Thanks, I struggled with that. I’ll try your suggestions.

                      1 Reply Last reply Reply Quote 0
                      • Alan KilbornA
                        Alan Kilborn @Paul Baker
                        last edited by Alan Kilborn

                        @Paul-Baker said in Is there a SCI_SETLINE type message (How to update a line):

                        I’m not a c++ guy

                        Maybe you should consider writing your plugin in a language you are comfortable in?
                        Caveat: Perhaps getting started without a template could be a hurdle…

                        Or consider scripting instead of a plugin?
                        There’s no shame in scripting. :-)

                        Paul BakerP 1 Reply Last reply Reply Quote 1
                        • Paul BakerP
                          Paul Baker @Paul Baker
                          last edited by

                          @Paul-Baker Updated without mem leaks

                          void updateCOBOLComments() {
                          
                          	// Get handle to Scintilla editor
                          	int currentEdit;
                          	::SendMessage(nppData._nppHandle, NPPM_GETCURRENTSCINTILLA, 0, (LPARAM)&currentEdit);
                          	HWND curScint = (currentEdit == 0) ? nppData._scintillaMainHandle : nppData._scintillaSecondHandle;
                          
                          	// Get selection start and end positions.
                          	// If selectionStr and selectionEnd are equal then there is no selection,
                          	// just process the line the caret is on.   
                          	int strSel = (int)::SendMessage(curScint, SCI_GETSELECTIONSTART, 0, 0);
                          	int endSel = (int)::SendMessage(curScint, SCI_GETSELECTIONEND, 0, 0);
                          
                          	// Get the line numbers associated with the start and end positions.
                          	// The strLine and endLine may be the same line.
                          	int strLine = (int)::SendMessage(curScint, SCI_LINEFROMPOSITION, strSel, 0);
                          	int endLine = (int)::SendMessage(curScint, SCI_LINEFROMPOSITION, endSel, 0);
                          
                          	size_t lineLen;
                          	std::string lineText = "";
                          
                          	int strPos;
                          	int endPos;
                          
                          	for (int lineNum = strLine; lineNum <= endLine; lineNum++) {
                          
                          		// Calculate line size by getting the line start and end positions
                          		strPos = (int) ::SendMessage(curScint, SCI_POSITIONFROMLINE, lineNum, 0);
                          		endPos = (int) ::SendMessage(curScint, SCI_GETLINEENDPOSITION, lineNum, 0);
                          
                          		// Get the current line. 
                          		lineLen = endPos - strPos + 1;
                          		lineText.resize(lineLen, ' ');
                          		::SendMessage(curScint, SCI_SETTARGETRANGE, strPos, endPos);
                          		::SendMessage(curScint, SCI_GETTARGETTEXT, 0, reinterpret_cast<LPARAM>(lineText.data()));
                          
                                           // *********************************
                                           // ** Modify lineText as needed., *
                                           // *********************************
                          
                          		::SendMessage(curScint, SCI_REPLACETARGET, static_cast<WPARAM>(-1), reinterpret_cast<LPARAM>(lineText.data()));
                          	}
                          
                          	return;
                          }
                          
                          rdipardoR 1 Reply Last reply Reply Quote 0
                          • Paul BakerP
                            Paul Baker @Alan Kilborn
                            last edited by

                            @Alan-Kilborn No shame… haha… I use to be a scripting basher (no pub intended). My comfortable language is java, then python. Imperative languages are all similar, if-then-else. There are lots of how to blogs for creating npp plugins using cpp and I did not want to make a project out of it. I’m sure there are easy ways to do this for many languages, I just took the path of least resistance. All comments appreciated. Thanks!

                            Mark OlsonM 1 Reply Last reply Reply Quote 1
                            • Mark OlsonM
                              Mark Olson @Paul Baker
                              last edited by

                              @Paul-Baker said in Is there a SCI_SETLINE type message (How to update a line):

                              My comfortable language is java

                              In that case I recommend this C# plugin template.

                              I’ve used it for two plugins and helped the development of another. C# is great, and very similar to Java.

                              Paul BakerP 1 Reply Last reply Reply Quote 0
                              • rdipardoR
                                rdipardo @Paul Baker
                                last edited by

                                @Paul-Baker said in Is there a SCI_SETLINE type message (How to update a line):

                                  /* . . . */
                                 int strSel = (int)::SendMessage(curScint, SCI_GETSELECTIONSTART, 0, 0);
                                 int endSel = (int)::SendMessage(curScint, SCI_GETSELECTIONEND, 0, 0);
                                

                                Those type casts are only safe for 32-bit plugins. Scintilla has supported multi-gigabyte files since 4.1.6 (the current version is 5.4.1). While a character position beyond 0x7fffffff isn’t likely, a 64-bit application would immediately crash if your plugin made one of the above calls in a document over 2 GB in size.

                                It’s best to store all position values as the dynamically sized Sci_Position type (an alias for intptr_t, which is what the API functions actually return).

                                The type definition is probably in your project files already; all recent versions of the main Scintilla header include Sci_Position.h.

                                Paul BakerP 1 Reply Last reply Reply Quote 4
                                • Paul BakerP
                                  Paul Baker @rdipardo
                                  last edited by

                                  @rdipardo Thank you!!! I’ll make that change.

                                  1 Reply Last reply Reply Quote 0
                                  • Paul BakerP
                                    Paul Baker @Mark Olson
                                    last edited by

                                    @Mark-Olson said in Is there a SCI_SETLINE type message (How to update a line):

                                    In that case I recommend this C# plugin template

                                    Hi Mark, thank you and everyone that has helped me get started with Visual Studio and NPP Plugin development. I looked into c# and yes it is easier.

                                    Here is the same logic I’ve been working on in c#. All works well.

                                            static void toggleCobolComment()
                                            {
                                                // Get selection start and end positions.
                                                // If selStr and selEnd are equal then there is no selection,
                                                // just process the line the caret is on.
                                                var strSel = editor.GetSelectionStart();
                                                var strEnd = editor.GetSelectionEnd();
                                    
                                                // Get the line numbers associated with the start and end positions.
                                                // The strLine and endLine may be the same line.
                                                var strLine = editor.LineFromPosition(strSel);
                                                var endLine = editor.LineFromPosition(strEnd);
                                    
                                                string lineText = "";
                                                char[] lineTextAsChars;
                                    
                                                int lineLen = 0;
                                    
                                                int strPos = 0;
                                                int endPos = 0;
                                                            
                                                 // Loop through each line and toggle the comment char "*"
                                                 // in column 7.  Skip lines that are shorter than 7 characters.
                                                for (var lineNum = strLine; lineNum <= endLine; lineNum++)
                                                {
                                                    // Calculate line size by getting the line start and end positions
                                                    strPos = editor.PositionFromLine(lineNum);
                                                    endPos = editor.GetLineEndPosition(lineNum);
                                    
                                                    // Get the current line length
                                                    lineLen = endPos - strPos + 1;
                                    
                                                    // Skip lines that are empty.
                                                    if (lineLen <= 6)
                                                        continue;
                                    
                                                    // Get line text and convert to char array
                                                    editor.SetTargetRange(strPos, endPos);
                                                    lineText = editor.GetTargetText();
                                                    lineTextAsChars = lineText.ToCharArray();
                                    
                                                    // Toggle comment character.
                                                    if (lineTextAsChars[6] == '*')
                                                        lineTextAsChars[6] = ' ';
                                                    else
                                                        lineTextAsChars[6] = '*';
                                    
                                                    // Convert char array back to string 
                                                    // and update line in editor. 
                                                    lineText = new string(lineTextAsChars);
                                                    editor.ReplaceTarget(lineText.Length, lineText);
                                                }
                                    
                                                return;
                                            }
                                    
                                    

                                    Thanks again… This thread is closed (for me).

                                    Mark OlsonM 1 Reply Last reply Reply Quote 1
                                    • Mark OlsonM
                                      Mark Olson @Paul Baker
                                      last edited by

                                      @Paul-Baker
                                      I’m glad it’s working!

                                      One note I would make (not just for you, for the general public) is that
                                      interacting with Scintilla is really slow compared to the .NET runtime and if you are concerned about performance, you will be much faster if you just slurp all the text out in one go, process it in C#, and then dump it all back into the file.

                                      Alan KilbornA 1 Reply Last reply Reply Quote 1
                                      • Alan KilbornA
                                        Alan Kilborn @Mark Olson
                                        last edited by Alan Kilborn

                                        @Mark-Olson said in Is there a SCI_SETLINE type message (How to update a line):

                                        much faster if you just slurp all the text out in one go, process it in C#, and then dump it all back into the file

                                        …and watch your change-history go “all orange”, registering every character in the file as “changed”. Secondary concern: It also nullifies a plugin’s opportunity to provide step-by-step undo for possible individual modifications it makes.
                                        For me this would be a big problem, and I wouldn’t use a plugin that did this to me.
                                        Sure, for some people this would be absolutely no problem.
                                        But I wouldn’t make this approach a general recommendation.

                                        1 Reply Last reply Reply Quote 3
                                        • First post
                                          Last post
                                        The Community of users of the Notepad++ text editor.
                                        Powered by NodeBB | Contributors