Author Topic: Code completion using LSP and clangd  (Read 312168 times)

Offline Pecan

  • Plugin developer
  • Lives here!
  • ****
  • Posts: 2813
Re: Code completion using LSP and clangd
« Reply #225 on: October 16, 2022, 01:57:16 am »
<Partial quote>
Hi, Pecan, In the latested r84 and with the change to using the std::string output of the both function writeClientLog() and writeServerLog().

I found an interesting thing that I got two different encoding output of the Chinese text in the comments.
One place is here, in the function: bool ProcessLanguageClient::WriteHdr(const std::string &in)
I see that the output of the content(usually the textDocument/didOpen message's content the client try to send to the LSP server) will be in GB2312 in my PC.
While, the content received from the server(usually the textDocument/hover message return from the LSP server) will be in UTF8 format.

I just exam the function body: bool ProcessLanguageClient::WriteHdr(), I see it still use the some kinds of std::string to wxString, and later some wxString to std::string, some conversion will use the default local encoding.

For example:
Code
limitedLogOut = wxString::Format("<<< Write():\n%s", wxString(in)).Mid(0,512) + "<...DATA SNIPED BY LOG WRITE()...>";

The variable in to wxString will use wrong encoding.

So, what I think is that we should use std::string inside the WriteHdr().
[/code]

Fixed in rev 85. At least I think so. I can't produce an environment that lets me accurately use chinese characters.

When I try to paste copied chinese chars into the editor it automatically converts them to utf8.

Tell me if it actually fixes this.

Thanks for testing and for the fixes.
« Last Edit: October 16, 2022, 02:00:15 am by Pecan »

Offline Pecan

  • Plugin developer
  • Lives here!
  • ****
  • Posts: 2813
Re: Code completion using LSP and clangd
« Reply #226 on: October 16, 2022, 02:06:03 am »

I've made a temporary fix (until the next Nightly) that circumvents the missing event.

Be careful though, the circumvention allows clangd to parse files while the "Run" command is active. You might want to right-click on the project name and "Pause parsing(toggle)" before running your program from the menu run command.

After the "Run" finishes, you can "Pause parsing(toggle)" again to continue parsing where it left off.


@pecan

At the moment I'm unable to replicate the bug, so the bug seems fixed.

Is there any inconvenience leaving the parser working while I'm running the application? I mean not considering the CPU usage?

Max

You can set the number of parsers you would like to run while compiling,running,debugging can be set at Settings/Editor/Clangd_client/"C/C++ parser"/Max number of threads while compiling.

But it really eats up the cpu. Set it to about 1/2 the number of processors/threads in your cpu.

Then sit back and feel it warm up the house and enjoy the aroma of melting plastic.

« Last Edit: October 16, 2022, 02:13:44 am by Pecan »

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6034
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Code completion using LSP and clangd
« Reply #227 on: October 16, 2022, 03:25:10 am »
<Partial quote>
Hi, Pecan, In the latested r84 and with the change to using the std::string output of the both function writeClientLog() and writeServerLog().

I found an interesting thing that I got two different encoding output of the Chinese text in the comments.
One place is here, in the function: bool ProcessLanguageClient::WriteHdr(const std::string &in)
I see that the output of the content(usually the textDocument/didOpen message's content the client try to send to the LSP server) will be in GB2312 in my PC.
While, the content received from the server(usually the textDocument/hover message return from the LSP server) will be in UTF8 format.

I just exam the function body: bool ProcessLanguageClient::WriteHdr(), I see it still use the some kinds of std::string to wxString, and later some wxString to std::string, some conversion will use the default local encoding.

For example:
Code
limitedLogOut = wxString::Format("<<< Write():\n%s", wxString(in)).Mid(0,512) + "<...DATA SNIPED BY LOG WRITE()...>";

The variable in to wxString will use wrong encoding.

So, what I think is that we should use std::string inside the WriteHdr().
[/code]

Fixed in rev 85. At least I think so. I can't produce an environment that lets me accurately use chinese characters.

When I try to paste copied chinese chars into the editor it automatically converts them to utf8.

Tell me if it actually fixes this.

Thanks for testing and for the fixes.

Hi, Pecan, I just tested this rev 85, and I can confirm that the encoding issue is fixed. In the log file, both the textDocument/didOpen and textDocument/hover messages are showing the same UTF8 encoding now.
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6034
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Code completion using LSP and clangd
« Reply #228 on: October 28, 2022, 12:40:23 pm »
Hi, Pecan, I try to debug clangd_client, but disable all the other plugins. So I have the patch here:

Re: How to exclude the other plugins when debugging

But when I have only two plugins to load, in the debug-plugin.conf file, I have such content:

Code
	<plugins>
<TRY_TO_ACTIVATE>
<str>
<![CDATA[]]>
</str>
</TRY_TO_ACTIVATE>
<INSTALL_GLOBALLY bool="1" />
<INSTALL_CONFIRMATION bool="1" />
<BATCH_BUILD_PLUGINS>
<astr>
<s>
<![CDATA[compiler.dll]]>
</s>
<s>
<![CDATA[clangd_client.dll]]>
</s>
</astr>
</BATCH_BUILD_PLUGINS>
<CLANGD_CLIENT bool="1" />
</plugins>

This means only those two dlls get loaded.

But when debugging, I see the debugee codeblocks will report that:

Code
[Window Title]
Clangd_client plugin

[Content]
The Clangd client plugin cannot run while the "Code completion" plugin is enabled.
The Clangd client plugin will now inactivate itself. :-(

If you wish to use the Clangd_client rather than the older CodeCompletion plugin,
navigate to Plugins->Manage plugins... and disable CodeCompletion, then enable Clangd_client.

Restart CodeBlocks after closing the "Manage plugins" dialog.

-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
Only one (Clangd_Client or CodeCompletion) should be enabled.

[OK]


In my plugin management dialog, I have only two plugins, so CodeCompletion plugin is not loaded. I'm not sure why in this case, clangd_client still report the codecompletion is enabled?

Thanks.


EDIT

It looks like the clangd_client has some logic to detect the codecompletion plugin, but the logic is not correct.

Now, I have a workaround, I have change the conf file to have this:

Code
	<plugins>
<TRY_TO_ACTIVATE>
<str>
<![CDATA[]]>
</str>
</TRY_TO_ACTIVATE>
<INSTALL_GLOBALLY bool="1" />
<INSTALL_CONFIRMATION bool="1" />
<BATCH_BUILD_PLUGINS>
<astr>
<s>
<![CDATA[compiler.dll]]>
</s>
<s>
<![CDATA[clangd_client.dll]]>
</s>
<s>
<![CDATA[codecompletion.dll]]>
</s>
</astr>
</BATCH_BUILD_PLUGINS>
<CLANGD_CLIENT bool="1" />
<CODECOMPLETION bool="0" />
</plugins>
Then, I can enable the clangd_client plugin correctly. (the codecompletion plugin should be loaded but disabled)

The situation is: if the codecompletion plugin never get loaded, I have no way to enable the clangd_client plugin.  :(

« Last Edit: October 28, 2022, 12:50:54 pm by ollydbg »
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6034
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Code completion using LSP and clangd
« Reply #229 on: October 28, 2022, 03:25:13 pm »
I see another crash caught by GDB.

Code
From b54738b2d2d2eec2eb7185f14f7c0e1f7c75c68f Mon Sep 17 00:00:00 2001
From: asmwarrior <a@b.com>
Date: Fri, 28 Oct 2022 21:22:01 +0800
Subject: delete the instance first, and later remove it from the list

If first remove from the list, the iterator becomes invalid, remember the pointer before the iterator get destroyed

diff --git a/clangd_client/src/codecompletion/parsemanager.cpp b/clangd_client/src/codecompletion/parsemanager.cpp
index 1590f31..9101ddd 100644
--- a/clangd_client/src/codecompletion/parsemanager.cpp
+++ b/clangd_client/src/codecompletion/parsemanager.cpp
@@ -713,11 +713,12 @@ bool ParseManager::DeleteParser(cbProject* project)
 
         // The logic here is : firstly delete the parser instance, then see whether we need an
         // active parser switch (call SetParser())
-        m_ParserList.erase(parserList_it); //remove deleted parser from parser list
-        delete parserList_it->second;
+        ParserBase* delParser = parserList_it->second;
+        delete parserList_it->second;      // delete the instance first, then remove from the list
+        m_ParserList.erase(parserList_it); // remove deleted parser from parser list
 
         // if the active parser is deleted, set the active parser to nullptr
-        if (parserList_it->second == m_Parser)
+        if (delParser == m_Parser)
         {
             m_Parser = nullptr;
             SetParser(m_TempParser); // Also updates class browser; do not use SetParser(m_TempParser) //(ph 2022/06/6)-


This will fix the crash.


EDIT:

I updated the patch, because I see the iterator get invalid, so we have to member the pointer address for later comparing.
« Last Edit: October 29, 2022, 06:28:04 am by ollydbg »
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline Pecan

  • Plugin developer
  • Lives here!
  • ****
  • Posts: 2813
Re: Code completion using LSP and clangd
« Reply #230 on: October 31, 2022, 07:13:03 pm »
Hi, Pecan, I try to debug clangd_client, but disable all the other plugins. So I have the patch here:

Re: How to exclude the other plugins when debugging

But when I have only two plugins to load, in the debug-plugin.conf file,
This means only those two dlls get loaded.

But when debugging, I see the debugee codeblocks will report that:

<...some content snipped by pecan...>

The Clangd client plugin cannot run while the "Code completion"
plugin is enabled.


In my plugin management dialog, I have only two plugins, so CodeCompletion plugin is not loaded. I'm not sure why in this case, clangd_client still report the codecompletion is enabled?

Thanks.


EDIT

It looks like the clangd_client has some logic to detect the codecompletion plugin, but the logic is not correct.

Now, I have a workaround, I have change the conf file to have this:

The situation is: if the codecompletion plugin never get loaded, I have no way to enable the clangd_client plugin.  :(

I've reworked the code that attempts to avoid running clangd_client when CodeCompletion is enabled.

This situation was caused by:
1) CodeCompletion.dll plugin is missing but the .conf say that it's enabled.
2) CodeCompletion is loaded by PluginManager but there's no info about it in the .conf file.

Hope I've solved these situations in clangd_client repo rev 87.

Offline Pecan

  • Plugin developer
  • Lives here!
  • ****
  • Posts: 2813
Re: Code completion using LSP and clangd
« Reply #231 on: October 31, 2022, 07:14:32 pm »
I see another crash caught by GDB.

Code
From b54738b2d2d2eec2eb7185f14f7c0e1f7c75c68f Mon Sep 17 00:00:00 2001
From: asmwarrior <a@b.com>
Date: Fri, 28 Oct 2022 21:22:01 +0800
Subject: delete the instance first, and later remove it from the list

If first remove from the list, the iterator becomes invalid, remember the pointer before the iterator get destroyed

diff --git a/clangd_client/src/codecompletion/parsemanager.cpp b/clangd_client/src/codecompletion/parsemanager.cpp
index 1590f31..9101ddd 100644
--- a/clangd_client/src/codecompletion/parsemanager.cpp
+++ b/clangd_client/src/codecompletion/parsemanager.cpp
@@ -713,11 +713,12 @@ bool ParseManager::DeleteParser(cbProject* project)
 
         // The logic here is : firstly delete the parser instance, then see whether we need an
         // active parser switch (call SetParser())
-        m_ParserList.erase(parserList_it); //remove deleted parser from parser list
-        delete parserList_it->second;
+        ParserBase* delParser = parserList_it->second;
+        delete parserList_it->second;      // delete the instance first, then remove from the list
+        m_ParserList.erase(parserList_it); // remove deleted parser from parser list
 
         // if the active parser is deleted, set the active parser to nullptr
-        if (parserList_it->second == m_Parser)
+        if (delParser == m_Parser)
         {
             m_Parser = nullptr;
             SetParser(m_TempParser); // Also updates class browser; do not use SetParser(m_TempParser) //(ph 2022/06/6)-


This will fix the crash.


EDIT:

I updated the patch, because I see the iterator get invalid, so we have to member the pointer address for later comparing.

Fix applied in repo rev 87.
Thanks

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6034
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Code completion using LSP and clangd
« Reply #232 on: November 01, 2022, 08:01:38 am »
...
I've reworked the code that attempts to avoid running clangd_client when CodeCompletion is enabled.

This situation was caused by:
1) CodeCompletion.dll plugin is missing but the .conf say that it's enabled.
2) CodeCompletion is loaded by PluginManager but there's no info about it in the .conf file.

Hope I've solved these situations in clangd_client repo rev 87.

I can confirm this issue is fixed, thanks!
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline killerbot

  • Administrator
  • Lives here!
  • *****
  • Posts: 5519
Re: Code completion using LSP and clangd
« Reply #233 on: November 01, 2022, 03:40:19 pm »
when we activate this new improved way in our nightly builds ?
Is it usable enough (I haven't tried it yet I must admit) to promote to a contrib plug-in ?
I

Offline Pecan

  • Plugin developer
  • Lives here!
  • ****
  • Posts: 2813
Re: Code completion using LSP and clangd
« Reply #234 on: November 01, 2022, 05:15:21 pm »
when we activate this new improved way in our nightly builds ?
Is it usable enough (I haven't tried it yet I must admit) to promote to a contrib plug-in ?
I

I'm comfortable with it now to say that it's ready to be added as a contrib.
I've used it everyday for more than a year.

If/when it become a contrib, it behaves/is guided by the presence of the old CodeCompletion plugin.
1) If CodeCompletion is enabled, Clangd_client disables itself.
2) If CB is a virgin install, and there's no compiler masterpath, Clangd_client disables itself.
3)If CodeCompletion is disabled, Clangd_client can run if the user enables it.
4)If CodeCompletion dll is missing, Clangd_client can run if the user enables it.
5) Otherwise, it remains asleep. waiting for user attention.

All these machinations are done to avoid any conflict with CodeCompletion and to give CodeCompletion priority. It requires the user to specifically take some actions:
1) Disable CodeCompletion and enable Clangd_client.
2) Provide a default compiler.
3) Provide access to a  clang/clangd location (such as llvm or Msys).

I'll provide instructions in the CB wiki regarding it's use and requirements.
Andrew has already provided most of the work for that wiki entry.

But I'd like to hear how other users feel about it becoming a contrib.
« Last Edit: November 01, 2022, 05:35:03 pm by Pecan »

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6034
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Code completion using LSP and clangd
« Reply #235 on: November 02, 2022, 01:51:15 am »
...
...
But I'd like to hear how other users feel about it becoming a contrib.
+1.

I use clangd_client plugin in my daily work for several months, it works quite well.
It gives more accurate code suggestion, code reference, code-refactoring(rename) feature than the old code completion plugin.
I have maintained the old code completion plugin for many years. The C++ language and its grammar is becoming so complex nowadays, so we need a compiler level parser. Currently only clang based tool has this kinds of feature.
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6034
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Code completion using LSP and clangd
« Reply #236 on: November 05, 2022, 09:14:41 am »
I see a bug that when the source code has TAB as the indent chars, our C::B editor will report the wrong position to clangd.

It looks like clangd just recognize the TAB as a single char, but in our C::B editor, when we get the column position, a TAB may be recognized as "4 space", in this case, when the clangd_client send the request to the LSP server, it send the wrong column position.

Do you see this bug?

To fix this issue, I think we have to adjust the column position we got from the editor. Any ideas?

EDIT:

The GetColumn() function has such document:

Retrieve the column number of a position, taking tab width into account.

EDIT2

In this page, we can get a "char" based column:
get column number Issue #75 jacobslusser/ScintillaNET

« Last Edit: November 05, 2022, 10:36:58 am by ollydbg »
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6034
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Code completion using LSP and clangd
« Reply #237 on: November 05, 2022, 10:44:34 am »
Other issue is that it looks like the icons are not showing correctly, it looks like the transparent color is shown in black?

See the image shot below:

If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline ollydbg

  • Developer
  • Lives here!
  • *****
  • Posts: 6034
  • OpenCV and Robotics
    • Chinese OpenCV forum moderator
Re: Code completion using LSP and clangd
« Reply #238 on: November 05, 2022, 11:16:17 am »
I see a bug that when the source code has TAB as the indent chars, our C::B editor will report the wrong position to clangd.

It looks like clangd just recognize the TAB as a single char, but in our C::B editor, when we get the column position, a TAB may be recognized as "4 space", in this case, when the clangd_client send the request to the LSP server, it send the wrong column position.

Do you see this bug?

To fix this issue, I think we have to adjust the column position we got from the editor. Any ideas?

EDIT:

The GetColumn() function has such document:

Retrieve the column number of a position, taking tab width into account.

EDIT2

In this page, we can get a "char" based column:
get column number Issue #75 jacobslusser/ScintillaNET

This issue can be fixed by this patch:

Code
From 8acd4b5b3bfe74d36583c54c4539dda112c6b1f3 Mon Sep 17 00:00:00 2001
From: asmwarrior <a@b.com>
Date: Sat, 5 Nov 2022 18:09:48 +0800
Subject: handling the char based column, do not take account the TAB width


diff --git a/clangd_client/src/LSPclient/client.cpp b/clangd_client/src/LSPclient/client.cpp
index 1654e1d..424fd0a 100644
--- a/clangd_client/src/LSPclient/client.cpp
+++ b/clangd_client/src/LSPclient/client.cpp
@@ -2301,7 +2301,10 @@ void ProcessLanguageClient::LSP_GoToDefinition(cbEditor* pcbEd, int argCaretPosi
     //-const int startPosn = pCtrl->WordStartPosition(edCaretPosn, true);
     //-const int endPosn   = pCtrl->WordEndPosition(posn, true); //not needed
     position.line       = edLineNum;
-    position.character  = edColumn;
+
+    int linePos = pCtrl->PositionFromLine(edLineNum); // Retrieve the position at the start of a line.
+    int fauxColumn = edCaretPosn - linePos;
+    position.character  = fauxColumn;
     writeClientLog(StdString_Format("<<< GoToDefinition:\n%s,line[%d], char[%d]", docuri.c_str(), position.line, position.character) );
 
     //Tell LSP server if text has changed
@@ -2386,7 +2389,9 @@ void ProcessLanguageClient::LSP_GoToDeclaration(cbEditor* pcbEd, int argCaretPos
     DocumentUri docuri = DocumentUri(stdFileURI.c_str());
     Position position;
     position.line       = edLineNum;
-    position.character  = edColumn;
+
+    int linePos = pCtrl->PositionFromLine(edLineNum); // Retrieve the position at the start of a line.
+    int fauxColumn = edCaretPosn - linePos;
     writeClientLog(StdString_Format("<<< GoToDeclaration:\n%s,line[%d], char[%d]", docuri.c_str(), position.line, position.character) );
 
     // Tell server if text has changed
@@ -2476,7 +2481,18 @@ void ProcessLanguageClient::LSP_FindReferences(cbEditor* pEd, int argCaretPositi
     //-int startPosn = pCtrl->WordStartPosition(caretPosn, true);
     //-const int endPosn   = pCtrl->WordEndPosition(posn, true); //not needed
     position.line       = edLineNum;
-    position.character  = edColumn;
+
+    // the clangd need char based column, where a TAB equals one char
+    // but GetColumn() function just return the column which retrieves the column number of a position, taking tab width into account.
+    // so we need a hack to get a new fauxColumn
+    // get column number Issue #75 jacobslusser/ScintillaNET - https://github.com/jacobslusser/ScintillaNET/issues/75
+    // var currentPos = scintilla.CurrentPosition;
+    // var currentLine = scintilla.LineFromPosition(currentPos);
+    // var linePos = scintilla.Lines[currentLine].Position;
+    // var fauxColumn = (currentPos - linePos);
+    int linePos = pCtrl->PositionFromLine(edLineNum); // Retrieve the position at the start of a line.
+    int fauxColumn = caretPosn - linePos;
+    position.character  = fauxColumn;
     writeClientLog(StdString_Format("<<< FindReferences:\n%s,line[%d], char[%d]", docuri.c_str(), position.line, position.character) );
 
     // Report changes to server else reported line references will be wrong.


I have one question: when I click the context menu->find reference. I see from the client log, there are also "goto declaration" and "goto definition" messages from the json RPC. Do we really need those extra 2 messages? I see find reference messages already correctly return the positions we needed.
If some piece of memory should be reused, turn them to variables (or const variables).
If some piece of operations should be reused, turn them to functions.
If they happened together, then turn them to classes.

Offline Pecan

  • Plugin developer
  • Lives here!
  • ****
  • Posts: 2813
Re: Code completion using LSP and clangd
« Reply #239 on: November 05, 2022, 09:37:37 pm »
Other issue is that it looks like the icons are not showing correctly, it looks like the transparent color is shown in black?

See the image shot below:

How are seeing those icons.
I don't remember displaying them in clangd_client.
Are you sure you're using clangd_client?

I really didn't want to use the icons. But instead use the extra column for useful data.
« Last Edit: November 05, 2022, 09:55:37 pm by Pecan »