python之移动端测试---appium

Appium

  • Appium介绍
    • 环境准备
    • 新版本appium的用法介绍
      • 元素定位函数被封装,统一使用By.xxx(定位方式):
      • 通过文本定位的写法
    • 一个简单的请求示例
    • APP操作api
      • 基础apk安装卸载
      • 发送,拉取文件
      • uiautomatorviewer工具使用
      • 获取页面元素及属性
      • 模拟事件操作
      • 模拟手机手势操作
        • 手机操作
      • 等待的三种方式
    • FAQ

Appium介绍

Appium是一个开源的自动化测试工具,其支持IOS、web、安卓平台上的应用。可以通过appium来控制安卓设备的操作(其实就是模拟人的行为,跟selenium类似)。

实现原理:
在这里插入图片描述

环境准备

1.安装jdk :sdk依赖于java环境。双击jdk默认安装即可

  • 配置环境变量:在高级–环境变量-添加JAVA_HOME,写入刚刚jdk的安装路径即可

2.安装android sdk:

  • 百度网盘搞个android_sdk包,解压至本地目录。然后配置环境变量:高级–环境变量-添加ANDROID_HOME,写入sdk的tools路径和platform-tools路径

3.手机(建议真机)或连接逍遥模拟器的方法:点这里!

我的逍遥模拟器地址:127.0.0.1:21513

4.电脑安装appium server:
方式一:可以直接下载exe包安装

方式二:通过命令行安装:

  • 先安装nodejs,下载地址nodejs.org.安装完成后通过node -v检查是否安装成功
  • 再安装cnpm :npm install -g cnpm --registry=https://registry.npm.taobao.org (由于npm国内下载较慢,
    建议使用淘宝镜像安装)
  • cnpm安装appium : cnpm install -g appium 如果报错runscriptError改为这样安装即可:npm install -g appium --registry=https://registry.npm.taobao.org
  • 启动appium : 直接命令行输入appium
  • 安装appium-python库:pip install Appium-Python-Client -----用来作为通过python操控手机的介质

备注:adb命令见:点这里!adb命令大全

新版本appium的用法介绍

元素定位函数被封装,统一使用By.xxx(定位方式):

def loginA(driver):loginBtn=driver.find_element(By.CLASS_NAME,'net.qiujuer.genius.ui.widget.Button')loginBtn.click()#账号:158*******8#密码:zx*****7accountBtn=driver.find_element(By.ID,'com.lockin.loock:id/et_phone_num')accountBtn.send_keys("158*******8")pwdBtn=driver.find_element(By.ID,'com.lockin.loock:id/et_password')pwdBtn.send_keys('zx*****7')checkBtn=driver.find_element(By.ID,'com.lockin.loock:id/checkbox_login')checkBtn.click()logBtn=driver.find_element(By.ID,'com.lockin.loock:id/btn_login')logBtn.click()print("账号登录成功")

通过文本定位的写法

def online(driver):#点击直播视图区域locateonline=driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,'new UiSelector().text("实时视频")')# locateonline=driver.find_element(By.A,' ')locateonline.click()

使用元素的文本内容(text)进行定位:
python
element = driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,‘new UiSelector().text(“文本内容”)’)

使用元素的部分文本内容(contains)进行定位:
python
element = driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,‘new UiSelector().textContains(“部分文本内容”)’)

使用元素的正则表达式匹配文本内容(matches)进行定位:
python
element = driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,‘new UiSelector().textMatches(“正则表达式”)’)

使用元素的开头文本内容(startsWith)进行定位:
python
element = driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,‘new UiSelector().textStartsWith(“开头文本内容”)’)

不同的自动化被测手机系统or 平台系统,需要切换不同的对应配置:
在这里插入图片描述


以下都是旧版本appium的使用方法

一个简单的请求示例

需求:打开安卓设备的一个app:
1.先设置好连接设备的信息,如设备平台,版本,设备号
2.获取app名称以及启动程序
3.通过appium发送请求

from appium import  webdriverdevices_info={}
#连接的参数获取,都可以通过adb命令来获取
#获取设备平台,版本,设备号。若是真实手机:完整的Device name要加上品牌和型号:比如这台设备时huawei的,型号是MAR_AL00,device name就是huawei-MAR_AL00_5G05475c0005devices_info['platformName']='Android'
devices_info['platformVersion']='5.1.1'
#模拟器的设备
devices_info['deviceName']='127.0.0.1:21503'#app信息:adb命令获取,记得一定要先把app开启。logcat | grep -i displayed
# devices_info['appPackage']='com.android.settings'
# devices_info['appActivity']='.Settings'
devices_info['appPackage']='com.songqin.sqoa'
devices_info['appActivity']='.MainActivity'  #应用启动名
#发送命令,连接手机
#这里的端口是appium的端口号
driver=webdriver.Remote('http://127.0.0.1:4723/wd/hub',devices_info)

ps:真机手机deviceName的获取方法:点我

APP操作api

基础apk安装卸载

import os
from appium import  webdriver#server启动参数
devices_info={}
#连接的参数获取
#获取设备平台,版本,设备号
devices_info['platformName']='Android'
devices_info['platformVersion']='5.1.1'
devices_info['deviceName']='127.0.0.1:21503'
devices_info['appPackage']='com.songqin.sqoa'
devices_info['appActivity']='.MainActivity'driver=webdriver.Remote('http://127.0.0.1:4723/wd/hub',devices_info)# 安装apk到手机上
driver.install_app(r'F:\Users\admin\PycharmProject\android_test\sqoa.apk')
#通用路径写法
apk_path=os.getcwd()+os.sep+'sqoa.apk'
driver.install_app(apk_path)#卸载apk,appid是需要卸载的app包名
app_id='com.sqoa.apk'
driver.remove_app(app_id)#判断app是否已安装
if driver.is_app_installed(app_id):print('该apk已被安装')driver.remove_app(app_id)
else:driver.install_app(apk_path)driver.quit()

发送,拉取文件

将本地pc文件通过push_file()方法发送到安卓设备上:
1.读取文件内容;
2.将读取到的byte类型数据先通过utf-8转成人看得懂的数据类型,然后再通过base64转码成手机可识别的数据类型,并对其进行编码,最后将其转为str类型。
3.通过push_file方法发送到手机,并指定文件的名称。

import base64
import os
from appium import  webdriverdevices_info={}
#连接的参数获取
#获取设备平台,版本,设备号
devices_info['platformName']='Android'
devices_info['platformVersion']='5.1.1'
devices_info['deviceName']='127.0.0.1:21503'
devices_info['appPackage']='com.songqin.sqoa'
devices_info['appActivity']='.MainActivity'driver=webdriver.Remote('http://127.0.0.1:4723/wd/hub',devices_info)# 发送文件到手机
with open('a.txt','r') as f:data=f.read()# 需要将读取到的byte类型数据转码发送b64_data=str(base64.b64encode(data.encode('utf-8')),'utf-8')# 发送文件到手机,并指定名字driver.push_file('/sdcard/hello.txt',b64_data)driver.close()

将设备上的文件拉取到本地,并且显示出数据

从安卓设备拉去文件并显示数据
sour_data=driver.pull_file('/sdcard/hello.txt')
# 解码base64数据
data=str(base64.b64decode(sour_data),'utf-8')
print(data)

获取页面源码关键字,做判断

# 获取页面源码
if '开发者选项' in driver.page_source:print("找到元素")
else;print("未找到元素")

uiautomatorviewer工具使用

简介:uiautomatorviewer就是用来扫描和分析Android应用程序的UI控件工具。

如何使用?
1.进入android_sdk的tools目录,打开uiautomatorviewer
2.电脑连接安卓设备或模拟器
3.点击启动待测试的app
4.点击uiautomatorviewer左上角device screenshot 会生成app的UI控件截图
5.选择截图上需要查看的控件,即可浏览该控件的id,calss ,text 坐标等信息

过程中给遇到双击闪退问题的解决方案,其他的都试过,只有这种方法最终才解决掉:https://blog.csdn.net/weixin_49554079/article/details/130013339

获取页面元素及属性

获取单个元素:
data=driver.find_element_by_id(“com.android.settings:id/title”)

data=driver.find_element_by_xpath(‘’//*[contains(@text,‘WLAN’)]‘’)

获取多个元素:
elements=driver.find_elements_by_class_name(“android.widget.TextView”)

在输入框中输入内容,清空内容

import base64
import os
from appium import  webdriverdevices_info={}
devices_info['platformName']='Android'
devices_info['platformVersion']='5.1.1'
devices_info['deviceName']='127.0.0.1:21503'
devices_info['appPackage']='com.songqin.sqoa'
devices_info['appActivity']='.MainActivity'driver=webdriver.Remote('http://127.0.0.1:4723/wd/hub',devices_info)input_content=driver.find_element_by_id("com.android.settings:id/search").send_keys("微信")input_content.clear()
driver.close_app()
driver.close()

获取元素值:
driver.find_element_by_id(“com.android.settings:id/search”).text

获取属性值:driver.find_element_by_id(“com.android.settings:id/search”)。get_attribute(“name”)

获取坐标:
driver.find_element_by_id(“com.android.settings:id/search”).location

获取包名、应用名
driver.current_package
driver.current_activity

模拟事件操作

滑动事件:
从一个坐标位置滑动到另一个坐标位置,是两点之间的滑动。

方法:swipe(start_x,start_y,end_x,end_y,duration=None)
参数说明:
1.start_x,start_y代表起点的x,y轴坐标
2.end_x,end_y代表终点的x,y轴坐标
3.duration代表滑动这个操作一共持续的时间长度。

wlan_location=driver.find_element_by_id("com.songqin.sqoa:id/title").location
version_location=driver.find_element_by_id("com.songqin.sqoa:id/version").location
driver.swipe(wlan_location['x'],wlan_location['y'],version_location['x'],version_location['y'],3000)

滚动事件:
从一个元素滚动到另一个元素,直到页面自动停止。

方法:
scroll(orgin_el,dest_el)
参数说明:
1.orgin_el代表开始元素位置,dest_el代表结束元素位置

wlan_location=driver.find_element_by_id("com.songqin.sqoa:id/title") 
version_location=driver.find_element_by_id("com.songqin.sqoa:id/version") 
driver.scroll(wlan_location,version_location)

拖拽事件drag:
将一个元素滑动到另一个元素,第二个元素替代第一个元素原本屏幕上的位置。

方法:
drag_and_drop(orgin_el,dest_el)
参数说明:
1.orgin_el代表开始元素位置,dest_el代表结束元素位置

wlan_location=driver.find_element_by_id("com.songqin.sqoa:id/title") 
version_location=driver.find_element_by_id("com.songqin.sqoa:id/version") 
driver.drag_and_drop(wlan_location,version_location)

将应用置于后台,模拟热启动:
driver.background_app(seconds)
seconds代表停留在后台的时间,单位s

模拟手机手势操作

模拟手点击屏幕操作:
TouchAction(driver).tap(element,x,y)
element:被定位到的元素
x:元素坐标的x轴数据
y:元素坐标的y轴数据

wlan=driver.find_element_by_id(“com.songqin.sqoa:id/title”)
#通过元素定位方式点击屏幕
TouchAction(driver).tap(wlan).perform()
#通过元素的x,y轴坐标来定位点击屏幕
TouchAction(driver).tap(wlan.location[‘x’],wlan.location[‘y’]).perform()

模拟手按下操作:
注意:一定要release当前的操作!!!否则会报错

方法:
TouchAction(driver).press(element,x,y)
release() #结束动作,手离开屏幕
wait()等待操作

参数说明:
element:被定位到的元素
x:元素坐标的x轴数据
y:元素坐标的y轴数据

wlan=driver.find_element_by_id("com.songqin.sqoa:id/title")
#按下操作
TouchAction(driver).press(wlan).release().perform()
#按下屏幕,等待3s,然后释放
TouchAction(driver).press(wlan).wait(3000).release().perform()
#长按3s
TouchAction(driver).long_press(id,id.location['x'],id.location['y'],3000).release().perform()

手按住屏幕向上拖动:
方法:
move_to(element=None,x=None,y=None)

wlan=driver.find_element_by_id("com.songqin.sqoa:id/title")
bluetooth=driver.find_element_by_id("com.songqin.sqoa:id/blue")
#根据元素方式拖动
TouchAction(driver).press(wlan).move_to(bluetooth).release().perform()
#根据坐标数据向上拖动
TouchAction(driver).press(x=200,y=1000).move_to(x=100,y=200).release().perform()
手机操作

获取手机时间:
driver.device_time

获取手机宽高
driver.get_window_size()

获取手机按钮:
driver.keyevent(24)
数字代表按键,参照adb的规则

打开手机通知栏:
driver.open_notifications()

获取手机网络
driver.network_connection
driver.set_network_connection(6) #将网络设置为ipv6
driver.network_connection
在这里插入图片描述

截图
driver.get_screencap_as_file(os.getcwd()+od.sep+‘hi.png’)

案例:
给手机设置图形锁密码

from appium import  webdriver
from appium.webdriver.common.touch_action import TouchActiondevices_info={}
devices_info['platformName']='Android'
devices_info['platformVersion']='5.1.1'
devices_info['deviceName']='127.0.0.1:21503'
devices_info['appPackage']='com.android.launcher3'
devices_info['appActivity']='.Launcher'driver=webdriver.Remote('http://127.0.0.1:4723/wd/hub',devices_info)
driver.implicitly_wait(30)kn=driver.find_element_by_xpath("//*[contains(@text,'知道了')]").click()
if "设置" in driver.page_source:#打开设置driver.find_element_by_xpath("//*[contains(@text,'设置')]").click()#滑动页面,使当前页面可以显示安全栏wlan=driver.find_element_by_xpath("//*[contains(@text,'WLAN')]")safe=driver.find_element_by_xpath("//*[contains(@text,'安全')]")TouchAction(driver).press(wlan).move_to(safe).release().perform()# 点击安全safe.click()#点击屏幕锁定、图案driver.find_element_by_xpath("//*[contains(@text,'屏幕锁定')]").click()driver.find_element_by_xpath("//*[contains(@text,'图案')]").click()#绘制图形TouchAction(driver).press(x=220,y=330).wait(500).move_to(x=220,y=550).wait(500)\.move_to(x=660,y=550).wait(500).move_to(x=870,y=970).release().perform()driver.close_app()
driver.close()

等待的三种方式

1.显示等待:WebDriverWait(driver,timeout,poll-frequency).until(method)
参数说明:
timeout:设置的超时时间
poll_frequency : 每次搜索间隔时间,默认为0.5s
method : 定位方法(一个匿名函数)

例:
WebDriverWait(driver,5,1).until(lambda x:x.find_element_by_id(id_value))
lambda函数中x代表传入的值driver
函数运行过程:
1.实例化WebDriverWait类,传入driver对象,之后driver对象被赋值给webdriverwait的一个类变量self.driver
2.until为webdriverwait的方法,until方法传入method方法,之后method方法会被传入self.driver
3,搜索到元素后until返回定位对象,没有搜索到函数until返回超时异常错误。

2.强制等待:time.sleep(5)
3.隐式等待:driver.implicitly(30)

FAQ

1.遇到UiAutomator exited unexpectedly with code 0, signal null
在这里插入图片描述

解决方案:可能是appium与uiautomator版本不兼容导致,更换请求为UIAutomator2即可。代码中配置:devices_info[‘automationName’]=‘UIAutomator2’

2.提示报错信息Original error: Could not find ‘apksigner.jar’

原因:android-sdk缺少apksigner.jar的jar依赖包
在该路径下增加apksigner.jar包:C:\Users\PC\AppData\Local\Android\Sdk

3.出现错误:Could not sign with default certificate

解决方案:以管理员身份打开appium

在这里插入图片描述

3.报错ziplagi

appium版本太旧与Android sdk不兼容导致,更新appium版本解决。我的appium版本更新到v2.5.1版本ok.

4.报错No route found for //session <-- POST //session 404 42 ms

新版本appium的请求默认地址修改了,因此访问的时候只要这样就可以http://127.0.0.1:4723,不用加后面的/wd/hub

5、uiautomatorviewer报错:Error while obtaining UI hierarchy XML file: com.android.ddmlib.SyncExcept…

1、在cmd命令窗口中输入 adb root 即可;
2、重启手机无效;
3、重新把手机拔了再插;
4、重启appuim;
5、重启uiautomatorviewer.bat;
6、打开手机开发者权限,将USB调试按钮重新启动就好了;
好吧,以上所有方法都试完了,如果解决了,恭喜你们,我不符合以上情况。
7、各总尝试后发现:关闭界面中的动态不停地进行绘制的元素-进度条(成功)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/66213.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

剑指Offer|LCR 021. 删除链表的倒数第 N 个结点

LCR 021. 删除链表的倒数第 N 个结点 给定一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;head [1], n 1…

基于物联网疫苗冷链物流监测系统设计

1. 项目开发背景 随着全球对疫苗运输要求的提高&#xff0c;特别是针对温度敏感型药品&#xff08;如疫苗&#xff09;的冷链管理&#xff0c;如何保证疫苗在运输过程中的温度、湿度、震动等环境因素的稳定性已成为亟需解决的问题。疫苗运输过程中&#xff0c;任何温度或湿度的…

软件逆向之标志位

进位标志CF&#xff08;Carry Flag&#xff09; 介绍&#xff1a;如果运算结果的最高位产生了一个进位&#xff08;加法&#xff09;或借位&#xff08;减法&#xff09;&#xff0c;那么&#xff0c;其值为1&#xff0c;否则其值为0。无符号数。 示例&#xff1a; mov al&…

【mybatis-plus问题集锦系列】mybatis使用xml配置文件实现数据的基础增删改查

简单的数据查询&#xff0c;我们可以在mapper接口里面去实现&#xff0c;但是如果是复杂的查询&#xff0c;我们就可以使用xml配置文件去做&#xff0c; 官网链接xml配置文件 实现效果 实现代码 根据mapper接口的包结构&#xff0c;在resources包里面新建同名同结构的xml文件…

(leetcode算法题)384. 打乱数组 398. 随机数索引

问题转化&#xff1a; 题目要求将nums中的数字出现的次序随机打乱 转化成&#xff1a;对于 0 号位置来说&#xff0c;nums[i], ..., nums[n - 1] 可以等概率的出现 ... && ... && 对于 n - 1号位置来说&#xff0c;nums[i], ..., nums[n - 1] 可以等概率的出…

Pycharm连接远程解释器

这里写目录标题 0 前言1 给项目添加解释器2 通过SSH连接3 找到远程服务器的torch环境所对应的python路径&#xff0c;并设置同步映射&#xff08;1&#xff09;配置服务器的系统环境&#xff08;2&#xff09;配置服务器的conda环境 4 进入到程序入口&#xff08;main.py&#…

kafka使用以及基于zookeeper集群搭建集群环境

一、环境介绍 zookeeper下载地址&#xff1a;https://zookeeper.apache.org/releases.html kafka下载地址&#xff1a;https://kafka.apache.org/downloads 192.168.142.129 apache-zookeeper-3.8.4-bin.tar.gz kafka_2.13-3.6.0.tgz 192.168.142.130 apache-zookee…

大模型系列18-AI Agents

什么是AI Agents Al Agent智能体&#xff0c;是指一种能够模拟人类思考和行为来自动执行任务&#xff0c;以解决复杂问题的程序或系统 架构图 思考->行动->观测 思考依赖记忆以及规划决策&#xff0c;行动依赖工具&#xff0c;观测依赖感知 举例 长沙今天白天和晚上的…

mysql自定义安装

1、下载安装包 我是在windows上安装&#xff0c;所以选择“Mysql Installer for Windows” 2、安装mysql 双击“mysql-installer-community-8.0.40.0.msi”&#xff0c;开始启动安装 这里选择安装项&#xff0c;这里只选择了两项。workbench是图形化管理工具&#xff0c;比较吃…

22408操作系统期末速成/复习(考研0基础上手)

第一部分:计算题&#xff1a; 考察范围&#xff1a;&#xff08;标红的是重点考&#xff09; 第一章&#xff1a;CPU利用率&#xff1a; 第二章&#xff1a; 进程调度算法&#xff08;需要注意不同调度算法的优先级和题目中给出的是否可以抢占【分为可抢占和不可抢占&#xff…

jquery实现的网页版扫雷小游戏源码

源码介绍 这是一款基于jQuery实现的经典扫雷小游戏源码&#xff0c;玩家根据游戏规则进行游戏&#xff0c;末尾再在确定的地雷位置单击右键安插上小红旗即可赢得游戏&#xff01;是一款非常经典的jQuery游戏代码。本源码改进了获胜之后的读数暂停功能。 效果预览 源码下载 j…

对计网大题的一些指正(中间介绍一下CDM的原理和应用)

目录 前言&#xff1a; &#xff08;1&#xff09;五层原理体系结构每层功能&#xff1a; 下面是文档的答案&#xff1a; 我在之前的博客里面有介绍过五层原理体系结构&#xff0c; 按理来说&#xff0c;第五层应该是应用层才对&#xff0c;而会话层的功能应该被放到应用层…

Arduino UNO 驱动1.8 TFT屏幕显示中文

背景 最近入手了一块1.8寸的tft屏幕&#xff0c;通过学习文档&#xff0c;已经掌握了接线&#xff0c;显示英文、数字、矩形区域、划线、画点等操作&#xff0c; 但是想显示中文的时候操作比较复杂。 问题 1、arduino uno 驱动这款屏幕目前使的是自带的<TFT.h> 库操作…

【论文阅读】Anchor-based fast spectral ensemble clustering

论文地址&#xff1a;Anchor-based fast spectral ensemble clustering - ScienceDirect 代码地址&#xff1a; 摘要 集成聚类通过融合多个基础聚类方法&#xff0c;可以获得更好且更稳健的结果&#xff0c;因此受到广泛关注。尽管近年来已经出现了许多代表性的算法&#xff…

检索增强生成 和思维链 结合: 如何创建检索增强思维链 (RAT)?

论文地址&#xff1a;https://arxiv.org/pdf/2403.05313 Github地址&#xff1a;https://github.com/CraftJarvis/RAT 想象一下&#xff0c;一个人工智能助手可以像莎士比亚一样写作&#xff0c;像专家一样推理。这听起来很了不起&#xff0c;对吧&#xff1f;但是&#xff0…

关于数组的一些应用--------数组作函数的返回值(斐波那契数列数列的实现)

数组在作为函数的返回值&#xff0c;一个很经典的例子就是获取斐波那契数列的前N项 代码思路&#xff1a; 设计思路 输入&#xff1a; 输入一个整数 n&#xff0c;表示要生成斐波那契数列的长度。 输出&#xff1a; 输出一个长度为 n 的整数数组&#xff0c;其中每个元素为斐…

【IT人物系列】之MySQL创始人

前言 当今世界有无数的人构成&#xff0c;其中有些人做了一些改变世界的事情&#xff0c;比如&#xff1a;乔布斯缔造了Apple帝国&#xff0c;‌詹姆斯高斯林创造了Java语言等。正是这些优秀的人做的这些优秀的事情&#xff0c;让这个世界更加美好。因此他们值得铭记。 从今天…

【2025最新计算机毕业设计】基于SpringBoot+Vue智慧养老医护系统(高质量源码,提供文档,免费部署到本地)【提供源码+答辩PPT+文档+项目部署】

作者简介&#xff1a;✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流。✌ 主要内容&#xff1a;&#x1f31f;Java项目、Python项目、前端项目、PHP、ASP.NET、人工智能…

源代码编译安装X11及相关库、vim,配置vim(2)

一、编译安装vim 编译时的cofigure选项如下.只有上一步的X11的包安装全了&#xff08;具体哪些是必须的&#xff0c;哪些是多余的没验证&#xff09;&#xff0c;configure才能认为X的库文件和头文件是可以用的 ./configure --prefixpwd/mybuild \--x-includes/path/to/X11/m…

LSP介绍并实现语言服务

首发于Enaium的个人博客 LSP (Language Server Protocol) 介绍 前段时间我为Jimmer DTO实现了一个 LSP 的语言服务&#xff0c;这是我第一次实现 LSP&#xff0c;所以在这里我分享一下我实现LSP的经验。 首先来看一下效果&#xff0c;图片太多&#xff0c;我就放一部分&#…