Skip to content

Commit 3c91705

Browse files
committed
Initial commit
0 parents  commit 3c91705

42 files changed

Lines changed: 17071 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# editorconfig.org
2+
root = true
3+
4+
[*]
5+
indent_style = space
6+
indent_size = 4
7+
end_of_line = crlf
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
insert_final_newline = true
11+
12+
[*.{md,rst,txt}]
13+
indent_size = 2

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
build
2+
.idea
3+
.vscode
4+
.history

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2018 <copyright holders>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<p align="center">
2+
<img src="src/PHPDocSearch.ico" width="100" height="100" />
3+
</p>
4+
5+
# Keypirinha Plugin: phpdocsearch
6+
7+
This is **phpdocsearch**, a plugin for the
8+
[Keypirinha](http://keypirinha.com) launcher.
9+
10+
Search for functions, classes and everything else related to the PHP documentation.
11+
12+
![Demo](usage.gif)
13+
14+
## Download
15+
16+
https://github.com/Fuhrmann/keypirinha-phpdocsearch/releases
17+
18+
## Installation
19+
20+
### With [PackageControl](https://github.com/ueffel/Keypirinha-PackageControl)
21+
22+
Install Package "keypirinha-phpdocsearch"
23+
24+
### Manually
25+
26+
* Download the `PHPDocSearch.keypirinha-package` from the [releases](https://github.com/Fuhrmann/keypirinha-phpdocsearch/releases/latest).
27+
* Copy the file into `%APPDATA%\Keypirinha\InstalledPackages` (installed mode) or
28+
`<Keypirinha_Home>\portable\Profile\InstalledPackages` (portable mode)
29+
30+
## Usage
31+
32+
Open Keypirinha and type 'phpdoc'. Once the suggestion appears press TAB or ENTER to open all suggestions.
33+
34+
## License
35+
36+
This package is distributed under the terms of the MIT license.

make.cmd

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
@echo off
2+
setlocal
3+
4+
set PACKAGE_NAME=PHPDocSearch
5+
set INSTALL_DIR=%APPDATA%\Keypirinha\InstalledPackages
6+
7+
if "%1"=="" goto help
8+
if "%1"=="-h" goto help
9+
if "%1"=="--help" goto help
10+
if "%1"=="help" (
11+
:help
12+
echo Usage:
13+
echo make help
14+
echo make clean
15+
echo make build
16+
echo make install
17+
echo make py [python_args]
18+
goto end
19+
)
20+
21+
if "%BUILD_DIR%"=="" set BUILD_DIR=%~dp0build
22+
if "%KEYPIRINHA_SDK%"=="" (
23+
echo ERROR: Keypirinha SDK environment not setup.
24+
echo Run SDK's "kpenv" script and try again.
25+
exit /b 1
26+
)
27+
28+
if "%1"=="clean" (
29+
if exist "%BUILD_DIR%" rmdir /s /q "%BUILD_DIR%"
30+
goto end
31+
)
32+
33+
if "%1"=="build" (
34+
if not exist "%BUILD_DIR%" mkdir "%BUILD_DIR%"
35+
pushd "%~dp0"
36+
call "%KEYPIRINHA_SDK%\cmd\kparch" ^
37+
"%BUILD_DIR%\%PACKAGE_NAME%.keypirinha-package" ^
38+
-r LICENSE* README* src
39+
popd
40+
goto end
41+
)
42+
43+
if "%1"=="install" (
44+
echo TODO: ensure the INSTALL_DIR variable declared at the top of this
45+
echo script complies to your configuration and remove this message
46+
exit /1
47+
48+
copy /Y "%BUILD_DIR%\*.keypirinha-package" "%INSTALL_DIR%\"
49+
goto end
50+
)
51+
52+
if "%1"=="py" (
53+
call "%KEYPIRINHA_SDK%\cmd\kpy" %2 %3 %4 %5 %6 %7 %8 %9
54+
goto end
55+
)
56+
57+
:end

src/ClassSuggestion.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import keypirinha as kp
2+
from .Suggestion import Suggestion
3+
4+
class ClassSuggestion(Suggestion):
5+
ITEMCAT = kp.ItemCategory.USER_BASE + 2
6+
7+
@classmethod
8+
def has_section_type(cls, section_type):
9+
return section_type in ["phpdoc:classref", "phpdoc:exceptionref"]

src/FunctionSuggestion.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import keypirinha as kp
2+
from .Suggestion import Suggestion
3+
4+
class FunctionSuggestion(Suggestion):
5+
ITEMCAT = kp.ItemCategory.USER_BASE + 3
6+
7+
@classmethod
8+
def has_section_type(cls, section_type):
9+
return section_type == "refentry"

src/GenericSuggestion.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import keypirinha as kp
2+
from .Suggestion import Suggestion
3+
4+
class GenericSuggestion(Suggestion):
5+
ITEMCAT = kp.ItemCategory.USER_BASE + 4
6+
7+
@classmethod
8+
def has_section_type(cls, section_type):
9+
return section_type not in ["phpdoc:classref", "phpdoc:exceptionref", "refentry"]

src/PHPDocSearch.ico

104 KB
Binary file not shown.

src/PHPDocSearch.py

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Keypirinha launcher (keypirinha.com)
2+
3+
import keypirinha as kp
4+
import keypirinha_util as kpu
5+
import keypirinha_net as kpnet
6+
from .ClassSuggestion import ClassSuggestion
7+
from .FunctionSuggestion import FunctionSuggestion
8+
from .Suggestion import Suggestion
9+
import json
10+
from datetime import datetime
11+
12+
import os, sys
13+
sys.path.append(os.path.join(os.path.dirname(__file__), "lib"))
14+
15+
from bs4 import BeautifulSoup
16+
17+
18+
class PHPDocSearch(kp.Plugin):
19+
"""
20+
Search and open php doc pages.
21+
"""
22+
# The php documentation website
23+
PHPDOC_WEBSITE = 'http://php.net'
24+
25+
# The path to the URL used to get the documentation index
26+
PHPDOC_SEARCHINDEX_URL = '/js/search-index.php'
27+
28+
# The name of the file inside cache folder used to save the search index from php.net website
29+
INDEX_FILENAME = 'searchindex.json'
30+
31+
# Number of days the documentation index should be updated
32+
DAYS_KEEP_CACHE = 7
33+
34+
def __init__(self):
35+
super().__init__()
36+
self.doc_index = []
37+
38+
def on_start(self):
39+
self.generate_search_index()
40+
self.get_documentation_index()
41+
self.create_actions()
42+
pass
43+
44+
def on_catalog(self):
45+
self.set_catalog([
46+
self.create_item(
47+
category=kp.ItemCategory.KEYWORD,
48+
label="PHP Documentation",
49+
short_desc="Search for functions, classes and more in the official PHP documentation",
50+
target="phpdoc",
51+
args_hint=kp.ItemArgsHint.REQUIRED,
52+
hit_hint=kp.ItemHitHint.KEEPALL
53+
)
54+
])
55+
56+
def on_suggest(self, user_input, items_chain):
57+
if not items_chain or items_chain[0].category() != kp.ItemCategory.KEYWORD:
58+
return
59+
60+
if len(user_input) > 0 and self.should_terminate(0.250):
61+
return
62+
63+
mylist = self.filterbyvalue(self.doc_index, user_input)
64+
if (len(mylist)):
65+
self.set_suggestions(mylist, kp.Match.ANY, kp.Sort.LABEL_ASC)
66+
67+
def filterbyvalue(self, seq, value):
68+
return list(filter(lambda item: value.lower() in item.label().lower(), seq))
69+
70+
def on_execute(self, item, action):
71+
data_bag = item.data_bag().split('|')
72+
url_to_open = "http://php.net/manual/en/{}.php".format(data_bag[0])
73+
74+
# Open in the browser
75+
if ((action and action.name() == 'open_url') or not action):
76+
kpu.web_browser_command(private_mode=None, new_window=None, url=url_to_open, execute=True)
77+
return
78+
79+
# Copy its signature
80+
if (action and (action.name() in ['copy_signature'])):
81+
opener = kpnet.build_urllib_opener()
82+
with opener.open(url_to_open) as request:
83+
response = request.read()
84+
85+
soup = BeautifulSoup(response, 'html.parser')
86+
87+
if (action.name() == 'copy_signature'):
88+
param_elements = soup.findAll("div", {"class": "methodsynopsis"})
89+
params = ', '.join(param.get_text() for param in param_elements)
90+
params = params.replace('\n', '')
91+
kpu.set_clipboard(params)
92+
self.log("Signature copied!")
93+
return
94+
95+
# Create the default actions to the suggestions
96+
def create_actions(self):
97+
general_actions = [
98+
self.create_action(name="open_url", label="Open in php.net", short_desc="Open the documentation in php.net")
99+
]
100+
101+
function_actions = [
102+
self.create_action(name="copy_signature", label="Copy signature", short_desc="Copy the function signature")
103+
]
104+
105+
self.set_actions(FunctionSuggestion.ITEMCAT, function_actions + general_actions)
106+
self.set_actions(ClassSuggestion.ITEMCAT, general_actions)
107+
108+
# Download the search index from php.net website and write it to plugin's cache folder
109+
def generate_search_index(self):
110+
search_index_filepath = self.get_search_index_path()
111+
112+
should_generate = False
113+
try:
114+
last_modified = datetime.fromtimestamp(os.path.getmtime(search_index_filepath)).date()
115+
if ((last_modified - datetime.today().date()).days > self.DAYS_KEEP_CACHE):
116+
should_generate = True
117+
except Exception as exc:
118+
should_generate = True
119+
120+
if not should_generate:
121+
return False
122+
123+
try:
124+
opener = kpnet.build_urllib_opener()
125+
with opener.open("{}{}".format(self.PHPDOC_WEBSITE, self.PHPDOC_SEARCHINDEX_URL)) as request:
126+
response = request.read()
127+
except Exception as exc:
128+
self.err("Could not reach the php documentation website to generate documentation index: ", exc)
129+
130+
data = json.loads(response)
131+
with open(search_index_filepath, "w") as index_file:
132+
json.dump(data, index_file, indent=2)
133+
134+
# Read the file containing the documentation index
135+
def get_documentation_index(self):
136+
if not self.doc_index:
137+
with open(self.get_search_index_path(), "r") as index_file:
138+
data = json.loads(index_file.read())
139+
for key in data:
140+
label = data[key][0]
141+
description = data[key][1]
142+
section_type = data[key][2]
143+
144+
doc = self.get_documentation_type(label, description, key, section_type)
145+
suggestion = self.create_item(
146+
category=doc.ITEMCAT,
147+
label=doc.label,
148+
short_desc=doc.desc,
149+
target=doc.label,
150+
data_bag="{}|{}".format(doc.url, doc.section_type),
151+
args_hint=kp.ItemArgsHint.FORBIDDEN,
152+
hit_hint=kp.ItemHitHint.IGNORE
153+
)
154+
155+
self.doc_index.append(suggestion)
156+
157+
return self.doc_index
158+
159+
# Returns the complete path of the file used as cache to the php documentation search index
160+
def get_search_index_path(self):
161+
cache_path = self.get_package_cache_path(True)
162+
return os.path.join(cache_path, self.INDEX_FILENAME)
163+
164+
def get_documentation_type(self, label, description, url, section_type):
165+
for cls in Suggestion.__subclasses__():
166+
if cls.has_section_type(section_type):
167+
return cls(label, description, url, section_type)
168+
raise ValueError

0 commit comments

Comments
 (0)