提交 094a469d 编写于 作者: M Matt Bierner

Working on adding comment api

上级 b4195abe
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/client/out/**/*.js"],
"preLaunchTask": "npm"
}
]
}
\ No newline at end of file
{
"version": "0.1.0",
"command": "npm",
"isShellCommand": true,
"showOutput": "silent",
"args": ["run", "compile"],
"isBackground": true,
"problemMatcher": "$tsc-watch"
}
\ No newline at end of file
src/**
tsconfig.json
\ No newline at end of file
# Git Extended
## Notices
Uses code from github desktop: https://github.com/desktop/desktop
\ No newline at end of file
{
"name": "git-extended",
"displayName": "Git Extended",
"description": "Git Extended",
"enableProposedApi": true,
"version": "0.0.1",
"publisher": "rebornix",
"engines": {
"vscode": "^1.13.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onView:commits",
"onView:stash",
"onView:pr"
],
"main": "./out/extension",
"contributes": {
"views": {
"explorer": [
{
"id": "pr",
"name": "Pull Requests"
}
]
},
"commands": [
{
"command": "commits.revertCommit",
"title": "Revert",
"icon": {
"dark": "resources/icons/dark/clean.svg",
"light": "resources/icons/light/clean.svg"
}
},
{
"command": "commits.refresh",
"title": "Refresh",
"icon": {
"dark": "resources/icons/dark/refresh.svg",
"light": "resources/icons/light/refresh.svg"
}
},
{
"command": "pr.refreshList",
"title": "Refresh",
"icon": {
"dark": "resources/icons/dark/refresh.svg",
"light": "resources/icons/light/refresh.svg"
}
},
{
"command": "stash.apply",
"title": "Apply Stash"
},
{
"command": "stash.delete",
"title": "Delete Stash"
},
{
"command": "stash.pop",
"title": "Pop Stash"
},
{
"command": "stash.stash",
"title": "Stash",
"icon": {
"dark": "resources/icons/dark/stage.svg",
"light": "resources/icons/light/stage.svg"
}
}
],
"menus": {
"view/title": [
{
"command": "commits.refresh",
"when": "view == commits",
"group": "navigation"
},
{
"command": "stash.stash",
"when": "view == stash",
"group": "navigation"
}
],
"view/item/context": [
{
"command": "stash.apply",
"when": "view == stash"
},
{
"command": "stash.pop",
"when": "view == stash"
},
{
"command": "stash.delete",
"when": "view == stash"
}
]
}
},
"scripts": {
"vscode:prepublish": "tsc -p ./",
"compile": "tsc -watch -p ./"
},
"devDependencies": {
"typescript": "^2.1.4",
"@types/node": "*"
},
"dependencies": {
"dugite": "^1.28.0",
"tmp": "^0.0.31",
"octokat": "^0.8.0",
"request": "^2.81.0",
"lodash": "4.17.5"
}
}
{
"description": "Extension to add task support for npm scripts.",
"displayName": "Npm support for VSCode",
"config.npm.autoDetect": "Controls whether auto detection of npm scripts is on or off. Default is on.",
"config.npm.runSilent": "Run npm commands with the `--silent` option.",
"config.npm.packageManager": "The package manager used to run scripts.",
"config.npm.exclude": "Configure glob patterns for folders that should be excluded from automatic script detection.",
"npm.parseError": "Npm task detection: failed to parse the file {0}",
"taskdef.script": "The npm script to customize.",
"taskdef.path": "The path to the folder of the package.json file that provides the script. Can be ommitted."
}
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-2 -2 16 16" enable-background="new -2 -2 16 16"><polygon fill="#C5C5C5" points="9,0 4.5,9 3,6 0,6 3,12 6,12 12,0"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-0.994 0 16 16" enable-background="new -0.994 0 16 16"><path fill="#C5C5C5" d="M13 6c0 1.461-.636 2.846-1.746 3.797l-5.584 4.951-1.324-1.496 5.595-4.962c.678-.582 1.061-1.413 1.061-2.29 0-1.654-1.345-3-2.997-3-.71 0-1.399.253-1.938.713l-1.521 1.287h2.448l-1.998 2h-3.996v-4l1.998-2v2.692l1.775-1.504c.899-.766 2.047-1.188 3.232-1.188 2.754 0 4.995 2.243 4.995 5z"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M27.459 14.902l-10.439-10.439c-.296-.294-.672-.452-1.089-.452-.417 0-.793.157-1.089.452l-2.248 2.247 2.549 2.549c.249-.112.522-.177.813-.177 1.106 0 2.002.896 2.002 2.002 0 .291-.064.565-.176.814l2.311 2.336c.25-.111.633-.234.923-.234 1.106 0 2 .911 2 2.016s-.894 1.969-2 1.969c-1.105-.001-2.016-.751-2.016-1.985 0-.28.016-.462.119-.704l-2.373-2.374-.023.007v6.274c.747.295 1.277 1.026 1.277 1.875 0 1.105-.878 2.016-1.984 2.016-1.104 0-2.031-.926-2.031-2.031 0-.846.535-1.564 1.28-1.857l.001-6.25c-.762-.282-1.309-1.009-1.309-1.871 0-.28.059-.546.162-.788l-2.555-2.557-7.115 7.114c-.599.601-.601 1.576.001 2.178l10.44 10.518c.296.295.671.45 1.089.45.415 0 .796-.159 1.089-.45l10.391-10.471c.601-.599.599-1.576 0-2.177z" fill="#fff"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#C5C5C5" d="M15 4v6h-2.276c.113-.318.187-.653.226-1h1.05v-5h-2v-2h-4v2.051c-.347.038-.681.112-1 .225v-3.276h5l3 3zm-7 8.949v1.051h-6v-7h2.276c.126-.354.28-.693.485-1h-3.761v9h8v-2.051c-.166.02-.329.051-.5.051l-.5-.051z"/><path fill="#75BEFF" d="M12 8.5c0-1.933-1.567-3.5-3.5-3.5s-3.5 1.567-3.5 3.5 1.567 3.5 3.5 3.5c.711 0 1.369-.215 1.922-.578l3.578 3.578 1-1-3.578-3.578c.363-.553.578-1.211.578-1.922zm-3.5 2.5c-1.381 0-2.5-1.119-2.5-2.5s1.119-2.5 2.5-2.5 2.5 1.119 2.5 2.5-1.119 2.5-2.5 2.5z"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon fill="#C5C5C5" points="10,2 7.414,2 8.414,3 9,3 9,3.586 9,4 9,4.414 9,6 12,6 12,13 4,13 4,8 3,8 3,14 13,14 13,5"/><polygon fill="#75BEFF" points="5,1 3,1 5,3 1,3 1,5 5,5 3,7 5,7 8,4"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M13.451 5.609l-.579-.939-1.068.812-.076.094c-.335.415-.927 1.341-1.124 2.876l-.021.165.033.163.071.345c0 1.654-1.346 3-3 3-.795 0-1.545-.311-2.107-.868-.563-.567-.873-1.317-.873-2.111 0-1.431 1.007-2.632 2.351-2.929v2.926s2.528-2.087 2.984-2.461h.012l3.061-2.582-4.919-4.1h-1.137v2.404c-3.429.318-6.121 3.211-6.121 6.721 0 1.809.707 3.508 1.986 4.782 1.277 1.282 2.976 1.988 4.784 1.988 3.722 0 6.75-3.028 6.75-6.75 0-1.245-.349-2.468-1.007-3.536z" fill="#2D2D30"/><path d="M12.6 6.134l-.094.071c-.269.333-.746 1.096-.91 2.375.057.277.092.495.092.545 0 2.206-1.794 4-4 4-1.098 0-2.093-.445-2.817-1.164-.718-.724-1.163-1.718-1.163-2.815 0-2.206 1.794-4 4-4l.351.025v1.85s1.626-1.342 1.631-1.339l1.869-1.577-3.5-2.917v2.218l-.371-.03c-3.176 0-5.75 2.574-5.75 5.75 0 1.593.648 3.034 1.695 4.076 1.042 1.046 2.482 1.694 4.076 1.694 3.176 0 5.75-2.574 5.75-5.75-.001-1.106-.318-2.135-.859-3.012z" fill="#C5C5C5"/></svg>
\ No newline at end of file
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><rect height="11" width="3" y="3" x="7" fill="#C5C5C5"/><rect height="3" width="11" y="7" x="3" fill="#C5C5C5"/></svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#3c8746" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
A
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#7F4E7E" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
C
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#692C77" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
C
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#9E121D" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
D
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#969696" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
I
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#1B80B2" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
M
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#CC6633" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
R
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#6C6C6C" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
U
</text>
</svg>
\ No newline at end of file
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><rect height="3" width="11" y="7" x="3" fill="#C5C5C5"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon points="5.382,13 2.382,7 6.618,7 7,7.764 9.382,3 13.618,3 8.618,13" fill="#F6F6F6"/><path d="M12 4l-4 8h-2l-2-4h2l1 2 3-6h2z" fill="#424242"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="-0.994 0 16 16" enable-background="new -0.994 0 16 16"><path fill="#424242" d="M13 6c0 1.461-.636 2.846-1.746 3.797l-5.584 4.951-1.324-1.496 5.595-4.962c.678-.582 1.061-1.413 1.061-2.29 0-1.654-1.345-3-2.997-3-.71 0-1.399.253-1.938.713l-1.521 1.287h2.448l-1.998 2h-3.996v-4l1.998-2v2.692l1.775-1.504c.899-.766 2.047-1.188 3.232-1.188 2.754 0 4.995 2.243 4.995 5z"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><defs><clipPath><path d="M.06 91.886h91.828v-91.886h-91.828v91.886z"/></clipPath><clipPath id="a"><path d="M0 0h92v92h-92v-92z"/></clipPath></defs><path d="M28.497 14.84l-11.354-11.353c-.653-.654-1.714-.654-2.368 0l-2.357 2.358 2.99 2.991c.695-.235 1.492-.077 2.046.477.557.558.713 1.361.473 2.059l2.882 2.882c.698-.241 1.502-.085 2.059.473.778.778.778 2.039 0 2.818-.779.779-2.04.779-2.819 0-.586-.586-.73-1.446-.434-2.167l-2.688-2.688v7.074c.19.094.369.219.527.377.778.778.778 2.039 0 2.819-.778.778-2.04.778-2.818 0-.778-.779-.778-2.04 0-2.819.192-.192.415-.338.653-.435v-7.14c-.237-.097-.46-.241-.653-.435-.589-.589-.731-1.455-.429-2.179l-2.948-2.949-7.785 7.785c-.654.655-.654 1.715 0 2.369l11.354 11.353c.654.654 1.714.654 2.369 0l11.3-11.301c.654-.654.654-1.715 0-2.369" fill="#424242" clip-path="url(#a)"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#656565" d="M15 4v6h-2.276c.113-.318.187-.653.226-1h1.05v-5h-2v-2h-4v2.051c-.347.038-.681.112-1 .225v-3.276h5l3 3zm-7 8.949v1.051h-6v-7h2.276c.126-.354.28-.693.485-1h-3.761v9h8v-2.051c-.166.02-.329.051-.5.051l-.5-.051z"/><path fill="#00539C" d="M12 8.5c0-1.933-1.567-3.5-3.5-3.5s-3.5 1.567-3.5 3.5 1.567 3.5 3.5 3.5c.711 0 1.369-.215 1.922-.578l3.578 3.578 1-1-3.578-3.578c.363-.553.578-1.211.578-1.922zm-3.5 2.5c-1.381 0-2.5-1.119-2.5-2.5s1.119-2.5 2.5-2.5 2.5 1.119 2.5 2.5-1.119 2.5-2.5 2.5z"/></svg>
\ No newline at end of file
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><polygon fill="#656565" points="10,2 7.414,2 8.414,3 9,3 9,3.586 9,4 9,4.414 9,6 12,6 12,13 4,13 4,8 3,8 3,14 13,14 13,5"/><polygon fill="#00539C" points="5,1 3,1 5,3 1,3 1,5 5,5 3,7 5,7 8,4"/></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M13.451 5.609l-.579-.939-1.068.812-.076.094c-.335.415-.927 1.341-1.124 2.876l-.021.165.033.163.071.345c0 1.654-1.346 3-3 3-.795 0-1.545-.311-2.107-.868-.563-.567-.873-1.317-.873-2.111 0-1.431 1.007-2.632 2.351-2.929v2.926s2.528-2.087 2.984-2.461h.012l3.061-2.582-4.919-4.1h-1.137v2.404c-3.429.318-6.121 3.211-6.121 6.721 0 1.809.707 3.508 1.986 4.782 1.277 1.282 2.976 1.988 4.784 1.988 3.722 0 6.75-3.028 6.75-6.75 0-1.245-.349-2.468-1.007-3.536z" fill="#F6F6F6"/><path d="M12.6 6.134l-.094.071c-.269.333-.746 1.096-.91 2.375.057.277.092.495.092.545 0 2.206-1.794 4-4 4-1.098 0-2.093-.445-2.817-1.164-.718-.724-1.163-1.718-1.163-2.815 0-2.206 1.794-4 4-4l.351.025v1.85s1.626-1.342 1.631-1.339l1.869-1.577-3.5-2.917v2.218l-.371-.03c-3.176 0-5.75 2.574-5.75 5.75 0 1.593.648 3.034 1.695 4.076 1.042 1.046 2.482 1.694 4.076 1.694 3.176 0 5.75-2.574 5.75-5.75-.001-1.106-.318-2.135-.859-3.012z" fill="#424242"/></svg>
\ No newline at end of file
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><rect height="11" width="3" y="3" x="7" fill="#424242"/><rect height="3" width="11" y="7" x="3" fill="#424242"/></svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#2d883e" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
A
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#9B4F96" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
C
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#682079" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
C
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#B9131A" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
D
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#969696" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
I
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#007ACC" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
M
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#CC6633" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
R
</text>
</svg>
\ No newline at end of file
<svg width="14px" height="14px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect fill="#6C6C6C" x="0" y="0" width="100" height="100" rx="35" ry="35"/>
<text x="50" y="75" font-size="75" text-anchor="middle" style="font-family: Menlo, Monaco, Consolas, &quot;Droid Sans Mono&quot;, &quot;Inconsolata&quot;, &quot;Courier New&quot;, monospace, &quot;Droid Sans Fallback&quot;;" fill="white">
U
</text>
</svg>
\ No newline at end of file
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><title>Layer 1</title><rect height="3" width="11" y="7" x="3" fill="#424242"/></svg>
\ No newline at end of file
import * as path from 'path';
import * as vscode from 'vscode';
import { Repository } from './common//models/repository';
import { getChangedFiles, getFile } from './common/file';
import { getCommits } from './common/log';
import { Commit } from './common/models/commit';
import { GitChangeType } from './common/models/file';
export class CommitTreeItem implements vscode.TreeItem {
readonly iconPath?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
constructor(
public readonly context: vscode.ExtensionContext,
public readonly label: string,
public readonly sha: string,
public readonly parentSHAs: ReadonlyArray<string>,
public readonly command?: vscode.Command,
public readonly collapsibleState?: vscode.TreeItemCollapsibleState
) {
this.collapsibleState = 1;
}
}
export class FileChangeItem implements vscode.TreeItem {
public iconPath?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
public filePath: string;
public sha: string;
public parentFilePath: string;
public parentSha: string;
public command?: vscode.Command;
public comments?: any[];
constructor(
public readonly label: string,
public readonly status: GitChangeType,
public readonly context: vscode.ExtensionContext,
public readonly fileName: string,
public readonly workspaceRoot?: string
) {
}
public populateCommandArgs() {
if (this.status === GitChangeType.MODIFY) {
this.command = {
title: 'show diff',
command: ShowDiffCommand.id,
arguments: [this]
};
} else if (this.status === GitChangeType.DELETE) {
this.command = {
title: 'show diff',
command: 'vscode.open',
arguments: [
vscode.Uri.file(path.resolve(this.workspaceRoot, this.parentFilePath))
]
};
} else {
this.command = {
title: 'show diff',
command: 'vscode.open',
arguments: [
vscode.Uri.file(path.resolve(this.workspaceRoot, this.filePath))
]
};
}
}
}
class ShowDiffCommand {
static readonly id = 'msgit.showDiff';
static run(item: FileChangeItem) {
vscode.commands.executeCommand('vscode.diff',
vscode.Uri.file(path.resolve(item.workspaceRoot, item.parentFilePath)),
vscode.Uri.file(path.resolve(item.workspaceRoot, item.filePath)),
item.fileName);
}
}
export class CommitsProvider implements vscode.TreeDataProvider<CommitTreeItem> {
private context: vscode.ExtensionContext;
private workspaceRoot: string;
private repository: Repository;
private icons: any;
private _onDidChangeTreeData: vscode.EventEmitter<CommitTreeItem | undefined> = new vscode.EventEmitter<CommitTreeItem | undefined>();
readonly onDidChangeTreeData: vscode.Event<CommitTreeItem | undefined> = this._onDidChangeTreeData.event;
activate(context: vscode.ExtensionContext, workspaceRoot: string, repository: Repository) {
this.context = context;
this.workspaceRoot = workspaceRoot;
this.repository = repository;
vscode.window.registerTreeDataProvider<CommitTreeItem>('commits', this);
this.icons = {
light: {
Modified: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-modified.svg')),
Added: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-added.svg')),
Deleted: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-deleted.svg')),
Renamed: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-renamed.svg')),
Copied: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-copied.svg')),
Untracked: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-untrackedt.svg')),
Ignored: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-ignored.svg')),
Conflict: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-conflict.svg')),
},
dark: {
Modified: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-modified.svg')),
Added: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-added.svg')),
Deleted: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-deleted.svg')),
Renamed: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-renamed.svg')),
Copied: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-copied.svg')),
Untracked: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-untracked.svg')),
Ignored: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-ignored.svg')),
Conflict: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-conflict.svg'))
}
};
vscode.commands.registerCommand('commits.refresh', async (args) => {
this._onDidChangeTreeData.fire();
});
vscode.commands.registerCommand('commits.revertCommit', async (element) => {
// TODO
// We can want to allow users to revert several commits
});
vscode.commands.registerCommand(ShowDiffCommand.id, ShowDiffCommand.run);
}
getTreeItem(element: CommitTreeItem | FileChangeItem): vscode.TreeItem {
if (element instanceof FileChangeItem) {
let iconUri: string;
let iconDarkUri: string;
switch (element.status) {
case GitChangeType.ADD:
iconUri = this.icons.light.Added;
iconDarkUri = this.icons.dark.Added;
break;
case GitChangeType.COPY:
iconUri = this.icons.light.Copied;
iconDarkUri = this.icons.dark.Copied;
break;
case GitChangeType.DELETE:
iconUri = this.icons.light.Deleted;
iconDarkUri = this.icons.dark.Deleted;
break;
case GitChangeType.MODIFY:
iconUri = this.icons.light.Modified;
iconDarkUri = this.icons.dark.Modified;
break;
case GitChangeType.RENAME:
iconUri = this.icons.light.Renamed;
iconDarkUri = this.icons.dark.Renamed;
break;
}
element.iconPath = {
light: iconUri,
dark: iconDarkUri
};
return element;
} else {
return element;
}
}
getChildren(element?: CommitTreeItem): Thenable<CommitTreeItem[]> {
if (!this.workspaceRoot) {
return Promise.resolve([]);
}
return new Promise(resolve => {
if (element) {
getChangedFiles(this.repository, element.sha).then(fileChanges => {
let promises = [];
let results = fileChanges.map(fileChange => {
let changedItem = new FileChangeItem(`${fileChange.filePath}`, fileChange.status, this.context, this.workspaceRoot);
if (fileChange.status === GitChangeType.MODIFY) {
promises.push(Promise.all([getFile(element.sha, fileChange.filePath).then(targetFile => {
changedItem.filePath = targetFile;
changedItem.sha = element.sha;
}), getFile(element.parentSHAs[0], fileChange.filePath).then(targetFile => {
changedItem.parentFilePath = targetFile;
changedItem.parentSha = element.parentSHAs[0];
})]).then(() => {
changedItem.populateCommandArgs();
}));
} else if (fileChange.status === GitChangeType.DELETE) {
promises.push(getFile(element.parentSHAs[0], fileChange.filePath).then(targetFile => {
changedItem.parentFilePath = targetFile;
changedItem.sha = element.parentSHAs[0];
changedItem.populateCommandArgs();
}));
} else {
promises.push(getFile(element.sha, fileChange.filePath).then(targetFile => {
changedItem.filePath = targetFile;
changedItem.sha = element.sha;
changedItem.populateCommandArgs();
}));
}
return changedItem;
});
Promise.all(promises).then(() => {
resolve(results as any); // TODO
});
});
} else {
getCommits(this.repository, 'HEAD', 100).then((commits: Commit[]) => {
let result = commits.map(commit => {
return new CommitTreeItem(this.context, `${commit.summary}`, commit.sha, commit.parentSHAs);
});
resolve(result);
}, (reason: any) => {
Promise.reject(reason);
});
}
});
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import { getFileContent, writeTmpFile } from './file';
import { GitChangeType, RichFileChange } from './models/file';
import { Repository } from './models/repository';
export const MODIFY_DIFF_INFO = /diff --git a\/(\S+) b\/(\S+).*\n*index.*\n*-{3}.*\n*\+{3}.*\n*((.*\n*)+)/;
export const NEW_FILE_INFO = /diff --git a\/(\S+) b\/(\S+).*\n*new file mode .*\nindex.*\n*-{3}.*\n*\+{3}.*\n*((.*\n*)+)/;
export const DELETE_FILE_INFO = /diff --git a\/(\S+) b\/(\S+).*\n*deleted file mode .*\nindex.*\n*-{3}.*\n*\+{3}.*\n*((.*\n*)+)/;
export const DIFF_HUNK_INFO = /@@ \-(\d+)(,(\d+))?( \+(\d+)(,(\d+)?))? @@/;
async function parseModifiedHunkComplete(originalContent, modifyDiffInfo, a, b) {
let left = originalContent.split(/\r|\n|\r\n/);
let diffHunks = modifyDiffInfo[3].split('\n');
diffHunks.pop(); // there is one additional line break at the end of the diff ??
let right = [];
let lastCommonLine = 0;
for (let i = 0; i < diffHunks.length; i++) {
let line = diffHunks[i];
if (DIFF_HUNK_INFO.test(line)) {
let changeInfo = DIFF_HUNK_INFO.exec(line);
let oriStartLine = Number(changeInfo[1]);
let oriEndLine = Number(changeInfo[3]) | 0;
for (let j = lastCommonLine + 1; j < oriStartLine; j++) {
right.push(left[j - 1]);
}
lastCommonLine = oriStartLine + oriEndLine - 1;
} else if (/^\+/.test(line)) {
right.push(line.substr(1));
} else {
let codeInFirstLine = line.substr(1);
right.push(codeInFirstLine);
}
}
if (lastCommonLine < left.length) {
for (let j = lastCommonLine + 1; j <= left.length; j++) {
right.push(left[j - 1]);
}
}
let contentPath = await writeTmpFile(right.join('\n'), path.extname(b));
let originalContentPath = await writeTmpFile(left.join('\n'), path.extname(a));
return new RichFileChange(contentPath, originalContentPath, GitChangeType.MODIFY, b);
}
async function parseModifiedHunkFast(modifyDiffInfo, a, b) {
let left = [];
let right = [];
let diffHunks = modifyDiffInfo[3].split('\n');
diffHunks.pop(); // there is one additional line break at the end of the diff ??
for (let i = 0; i < diffHunks.length; i++) {
let line = diffHunks[i];
if (/@@ \-(\d+)(,(\d+))?( \+(\d+)(,(\d+)?))? @@/.test(line)) {
// let changeInfo = /@@ \-(\d+)(,(\d+))?( \+(\d+)(,(\d+)?))? @@/.exec(line);
left.push(line);
right.push(line);
} else if (/^\-/.test(line)) {
left.push(line.substr(1));
} else if (/^\+/.test(line)) {
right.push(line.substr(1));
} else {
let codeInFirstLine = line.substr(1);
left.push(codeInFirstLine);
right.push(codeInFirstLine);
}
}
let contentPath = await writeTmpFile(right.join('\n'), path.extname(b));
let originalContentPath = await writeTmpFile(left.join('\n'), path.extname(a));
return new RichFileChange(contentPath, originalContentPath, GitChangeType.MODIFY, b);
}
export async function parseDiff(text: string, repository: Repository, parentCommit: string) {
let reg = /diff((?!diff).*\n*)*/g;
let match = reg.exec(text);
let richFileChanges: RichFileChange[] = [];
while(match) {
let singleFileDiff = match[0];
let modifyDiffInfo = MODIFY_DIFF_INFO.exec(singleFileDiff);
if (modifyDiffInfo) {
let a = modifyDiffInfo[1];
let b = modifyDiffInfo[2];
try {
let originalContent = await getFileContent(repository.path, parentCommit, a);
let richFileChange = await parseModifiedHunkComplete(originalContent, modifyDiffInfo, a, b);
richFileChanges.push(richFileChange);
} catch (e) {
let richFileChange = await parseModifiedHunkFast(modifyDiffInfo, a, b);
richFileChanges.push(richFileChange);
}
match = reg.exec(text);
continue;
}
let newDiffInfo = NEW_FILE_INFO.exec(singleFileDiff);
if (newDiffInfo) {
let fileName = newDiffInfo[1];
let diffHunks = newDiffInfo[3].split('\n');
let contentArray = [];
for (let i = 0; i < diffHunks.length; i++) {
if (/@@ \-(\d+)(,(\d+))?( \+(\d+)(,(\d+)?))? @@$/.test(diffHunks[i])) {
continue;
} else if (/@@ \-(\d+)(,(\d+))?( \+(\d+)(,(\d+)?))? @@ /.test(diffHunks[i])) {
contentArray.push(diffHunks[i].replace(/@@ \-(\d+)(,(\d+))?( \+(\d+)(,(\d+)?))? @@ /, ''));
} else if (/^\+/.test(diffHunks[i])) {
contentArray.push(diffHunks[i].substr(1));
}
}
let filePath = await writeTmpFile(contentArray.join('\n'), path.extname(fileName));
let richFileChange = new RichFileChange(filePath, filePath, GitChangeType.ADD, fileName);
richFileChanges.push(richFileChange);
match = reg.exec(text);
continue;
}
let deleteDiffInfo = DELETE_FILE_INFO.exec(singleFileDiff);
if (deleteDiffInfo) {
let fileName = deleteDiffInfo[1];
let diffHunks = deleteDiffInfo[3].split('\n');
let contentArray = [];
for (let i = 0; i < diffHunks.length; i++) {
if (/@@ \-(\d+)(,(\d+))?( \+(\d+)(,(\d+)?))? @@$/.test(diffHunks[i])) {
continue;
} else if (/@@ \-(\d+)(,(\d+))?( \+(\d+)(,(\d+)?))? @@ /.test(diffHunks[i])) {
contentArray.push(diffHunks[i].replace(/@@ \-(\d+)(,(\d+))?( \+(\d+)(,(\d+)?))? @@ /, ''));
} else if (/^\-/.test(diffHunks[i])) {
contentArray.push(diffHunks[i].substr(1));
}
}
let originalFilePath = await writeTmpFile(contentArray.join('\n'), path.extname(fileName));
let filePath = await writeTmpFile('', path.extname(fileName));
let richFileChange = new RichFileChange(filePath, originalFilePath, GitChangeType.DELETE, fileName);
richFileChanges.push(richFileChange);
match = reg.exec(text);
continue;
}
match = reg.exec(text);
}
return richFileChanges;
}
\ No newline at end of file
import * as fs from 'fs';
import * as path from 'path';
import * as tmp from 'tmp';
import * as vscode from 'vscode';
import { GitProcess } from 'dugite';
import { Repository } from './models/repository';
import { SlimFileChange, GitChangeType, fromStatus } from './models/file';
/**
* @param content
*
* a
*
* c
*
* b
*
*
* @param ext
*/
export async function writeTmpFile(content: string, ext: string): Promise<string> {
return new Promise<string>((resolve, reject) => {
tmp.file({ postfix: ext }, async (err: any, tmpFilePath: string) => {
if (err) {
reject(err);
return;
}
try {
fs.appendFileSync(tmpFilePath, content);
resolve(tmpFilePath);
} catch (ex) {
reject(ex);
}
});
})
}
export async function getFile(commitSha1: string, localFilePath: string): Promise<string> {
const rootDir = vscode.workspace.rootPath;
return new Promise<string>((resolve, reject) => {
if (commitSha1 === undefined) {
resolve('fileUnavailable');
return;
}
let ext = path.extname(localFilePath);
tmp.file({ postfix: ext }, async (err: any, tmpFilePath: string) => {
if (err) {
reject(err);
return;
}
try {
let data = await getFileContent(rootDir, commitSha1, localFilePath);
fs.appendFileSync(tmpFilePath, data);
resolve(tmpFilePath);
}
catch (ex) {
console.log(ex);
reject(ex);
}
});
});
}
export async function getFileContent(rootDir: string, commitSha: string, sourceFilePath: string): Promise<string> {
const result = await GitProcess.exec([
'show',
`${commitSha}:` + sourceFilePath.replace(/\\/g, '/')
], rootDir);
const out = result.stdout;
const error = result.stderr;
if (result.exitCode === 0) {
return out;
} else {
throw error;
}
}
export async function getChangedFiles(repository: Repository, sha: string): Promise<ReadonlyArray<SlimFileChange>> {
const args = ['log', sha, '--name-status', '--format=format:', '-z', '-1'];
const result = await GitProcess.exec(args, repository.path);
const out = result.stdout;
const lines = out.split('\0');
lines.splice(-1, 1);
const files: SlimFileChange[] = [];
for (let i = 0; i < lines.length; i++) {
const statusText = lines[i];
const status = fromStatus(statusText);
let originalPath: string | undefined = undefined;
if (status === GitChangeType.RENAME || status === GitChangeType.COPY) {
originalPath = lines[++i];
}
const path = lines[++i];
files.push(new SlimFileChange(path, originalPath, status, null));
}
return files;
}
\ No newline at end of file
import { Repository } from './models/repository';
import { Commit } from './models/commit';
import { GitProcess } from 'dugite';
export async function getParentCommit(repository: Repository, sha: string): Promise<string> {
const result = await GitProcess.exec(
[
'rev-list',
'--parents',
'-n',
'1',
sha
],
repository.path
);
let commits = result.stdout.split(' ');
if (commits.length > 2) {
return commits[1];
} else {
return null;
}
}
export async function getCommits(repository: Repository, revisionRange: string, limit: number, additionalArgs: ReadonlyArray<string> = []): Promise<Commit[]> {
const delimiter = '1F';
const delimiterString = String.fromCharCode(parseInt(delimiter, 16));
const prettyFormat = [
'%H', // SHA
'%s', // summary
'%P', // parent SHAs
].join(`%x${delimiter}`);
const result = await GitProcess.exec([
'log',
revisionRange,
`--max-count=${limit}`,
`--pretty=${prettyFormat}`,
'-z',
...additionalArgs,
], repository.path);
const out = result.stdout;
const lines = out.split('\0');
lines.splice(-1, 1);
const commits = lines.map(line => {
const pieces = line.split(delimiterString);
const sha = pieces[0];
const summary = pieces[1];
const shaList = pieces[2];
const parentSHAs = shaList.length ? shaList.split(' ') : [];
return new Commit(sha, summary, parentSHAs);
});
return commits;
}
export async function isWorkingTreeClean(repository: Repository): Promise<boolean> {
const result = await GitProcess.exec(
[
'diff-index',
'--quiet',
'HEAD',
'--'
],
repository.path
);
let exitCode = result.exitCode;
if (exitCode !== 0) {
return false;
} else {
return true;
}
}
\ No newline at end of file
interface DiffHunkRange {
originalStart: number;
originalLength: number;
start: number;
length: number;
}
interface User {
id: string;
login: string;
}
export interface Comment {
url: string;
id: string;
path: string;
pull_request_review_id: string;
diff_hunk_range: DiffHunkRange;
position: number;
originalPosition: number;
commit_id: string;
original_commit_id: string;
user: User;
body: string;
created_at: string;
updated_at: string;
html_url: string;
}
export class Commit {
constructor(public sha: string, public summary: string, public parentSHAs: string[]) { }
}
\ No newline at end of file
export enum GitChangeType {
ADD,
COPY,
DELETE,
MODIFY,
RENAME,
TYPE,
UNKNOWN,
UNMERGED
}
export function fromStatus(status: string): GitChangeType {
switch (status) {
case 'A': return GitChangeType.ADD;
case 'C': return GitChangeType.COPY;
case 'D': return GitChangeType.DELETE;
case 'M': return GitChangeType.MODIFY;
case 'R': return GitChangeType.RENAME;
case 'T': return GitChangeType.TYPE;
case 'X': return GitChangeType.UNKNOWN;
case 'U': return GitChangeType.UNMERGED;
}
if (status.match(/R[0-9]+/)) { return GitChangeType.RENAME; }
if (status.match(/C[0-9]+/)) { return GitChangeType.COPY; }
return GitChangeType.MODIFY;
}
export class SlimFileChange {
public originalContent: string;
public content: string;
constructor(
public readonly filePath: string,
public readonly originalFilePath: string,
public readonly status: GitChangeType,
public readonly fileName: string
) { }
}
export class RichFileChange {
constructor(
public readonly filePath: string,
public readonly originalFilePath: string,
public readonly status: GitChangeType,
public readonly fileName: string
) { }
}
\ No newline at end of file
export interface IRemote {
readonly name: string;
readonly url: string;
}
export interface IGitRemoteURL {
/** The hostname of the remote. */
readonly hostname: string;
/**
* The owner of the GitHub repository. This will be null if the URL doesn't
* take the form of a GitHub repository URL (e.g., owner/name).
*/
readonly owner: string | null;
/**
* The name of the GitHub repository. This will be null if the URL doesn't
* take the form of a GitHub repository URL (e.g., owner/name).
*/
readonly name: string | null;
}
import { IGitRemoteURL } from './remote';
export class GitHubRepository {
constructor(
public name: string,
public owner: string,
public url: string
) { }
}
export class Repository {
public path: string;
public remotes: IGitRemoteURL[];
constructor(path: string, remotes: IGitRemoteURL[]) {
this.path = path;
this.remotes = remotes;
}
}
\ No newline at end of file
import { GitProcess } from 'dugite';
import { IGitRemoteURL } from './models/remote';
export async function getRemotes(
path: string
) {
const result = await GitProcess.exec(['remote', '-v'], path);
const output = result.stdout;
const lines = output.split('\n');
const remotes = lines
.filter(x => x.endsWith('(fetch)'))
.map(x => x.split(/\s+/))
.map(x => ({ name: x[0], url: x[1] }));
return remotes;
}
/** Parse the remote information from URL. */
export function parseRemote(url: string): IGitRemoteURL | null {
// Examples:
// https://github.com/octocat/Hello-World.git
// https://github.com/octocat/Hello-World.git/
// git@github.com:octocat/Hello-World.git
// git:github.com/octocat/Hello-World.git
const regexes = [
new RegExp('^https?://(?:.+@)?(.+)/(.+)/(.+?)(?:/|.git/?)?$'),
new RegExp('^git@(.+):(.+)/(.+?)(?:/|.git)?$'),
new RegExp('^git:(.+)/(.+)/(.+?)(?:/|.git)?$'),
new RegExp('^ssh://git@(.+)/(.+)/(.+?)(?:/|.git)?$')
];
for (const regex of regexes) {
const result = url.match(regex);
if (!result) {
continue;
}
const hostname = result[1];
const owner = result[2];
const name = result[3];
if (hostname) {
return { hostname, owner, name };
}
}
return null;
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as vscode from 'vscode';
import { CommitsProvider } from './commitsProvider';
import { PRProvider } from './prProvider';
import { Repository } from './common/models/repository';
import { getRemotes, parseRemote } from './common/remote';
export async function activate(context: vscode.ExtensionContext) {
const rootPath = vscode.workspace.rootPath;
const remotes = await getRemotes(rootPath);
const remoteUrls = remotes.map(remote => parseRemote(remote.url));
const repository = new Repository(rootPath, remoteUrls);
new CommitsProvider().activate(context, rootPath, repository);
new PRProvider().activate(context, rootPath, repository);
}
\ No newline at end of file
import * as vscode from 'vscode';
// import { Octokat } from 'octokat';
var Octokat = require('octokat');
// import * as https from 'https';
// import * as fs from 'fs';
// import * as tmp from 'tmp';
import * as path from 'path';
import * as request from 'request';
import { FileChangeItem } from './commitsProvider';
import { parseDiff, DIFF_HUNK_INFO } from './common/diff';
import { GitChangeType } from './common/models/file';
import { Repository } from './common//models/repository';
import { Comment } from './common/models/comment';
import * as _ from 'lodash';
export class PullRequest {
constructor(public octoPRItem: any) { };
}
export class PRProvider implements vscode.TreeDataProvider<PullRequest | FileChangeItem>, vscode.CommentProvider {
private _fileChanges: FileChangeItem[];
private _comments?: Comment[];
private context: vscode.ExtensionContext;
private workspaceRoot: string;
private repository: Repository;
private octo: any;
private icons: any;
constructor() {
this.octo = new Octokat();
}
activate(context: vscode.ExtensionContext, workspaceRoot: string, repository: Repository) {
this.context = context;
this.workspaceRoot = workspaceRoot;
this.repository = repository;
vscode.window.registerTreeDataProvider<PullRequest | FileChangeItem>('pr', this);
this.icons = {
light: {
Modified: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-modified.svg')),
Added: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-added.svg')),
Deleted: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-deleted.svg')),
Renamed: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-renamed.svg')),
Copied: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-copied.svg')),
Untracked: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-untrackedt.svg')),
Ignored: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-ignored.svg')),
Conflict: context.asAbsolutePath(path.join('resources', 'icons', 'light', 'status-conflict.svg')),
},
dark: {
Modified: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-modified.svg')),
Added: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-added.svg')),
Deleted: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-deleted.svg')),
Renamed: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-renamed.svg')),
Copied: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-copied.svg')),
Untracked: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-untracked.svg')),
Ignored: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-ignored.svg')),
Conflict: context.asAbsolutePath(path.join('resources', 'icons', 'dark', 'status-conflict.svg'))
}
};
vscode.workspace.registerCommentProvider(this);
}
getTreeItem(element: PullRequest | FileChangeItem): vscode.TreeItem {
if (element instanceof PullRequest) {
return {
label: element.octoPRItem.title,
collapsibleState: 1
};
} else {
let iconUri: string;
let iconDarkUri: string;
switch (element.status) {
case GitChangeType.ADD:
iconUri = this.icons.light.Added;
iconDarkUri = this.icons.dark.Added;
break;
case GitChangeType.COPY:
iconUri = this.icons.light.Copied;
iconDarkUri = this.icons.dark.Copied;
break;
case GitChangeType.DELETE:
iconUri = this.icons.light.Deleted;
iconDarkUri = this.icons.dark.Deleted;
break;
case GitChangeType.MODIFY:
iconUri = this.icons.light.Modified;
iconDarkUri = this.icons.dark.Modified;
break;
case GitChangeType.RENAME:
iconUri = this.icons.light.Renamed;
iconDarkUri = this.icons.dark.Renamed;
break;
}
element.iconPath = {
light: iconUri,
dark: iconDarkUri
};
return element;
}
}
getChildren(element?: PullRequest): PullRequest[] | Thenable<PullRequest[]> | FileChangeItem[] | Thenable<FileChangeItem[]> {
if (element) {
return new Promise<FileChangeItem[]>((resolve, rxeject) => {
request({
followAllRedirects: true,
url: element.octoPRItem.diffUrl
}, async (error, response, body) => {
// map comments to FileChangeItem
// registerCommentProvider
const rawComments = await element.octoPRItem.reviewComments.fetch();
const comments: Comment[] = parseComments(rawComments.items);
let richContentChanges = await parseDiff(body, this.repository, element.octoPRItem.base.sha);
let fileChanges = richContentChanges.map(change => {
let changedItem = new FileChangeItem(change.fileName ? change.fileName : change.filePath, change.status, this.context, change.fileName, this.workspaceRoot);
changedItem.filePath = change.filePath;
changedItem.parentFilePath = change.originalFilePath;
changedItem.populateCommandArgs();
return changedItem;
});
this._fileChanges = fileChanges;
this._comments = comments;
vscode.workspace.onDidOpenTextDocument(e => {
let matchingComments = getMatchingCommentsForDiffViewEditor(e.fileName, fileChanges, comments);
console.log('---diff editor---')
for (let i = 0; i < matchingComments.length; i++) {
let cm = matchingComments[i];
console.log(`after line: ${cm.diff_hunk_range.start + cm.position - 1 - 1}, ${'@' + cm.user.login}: '${cm.body}'`);
}
matchingComments = getMatchingCommentsForNormalEditor(e.fileName, this.workspaceRoot, comments);
console.log('---editor---')
for (let i = 0; i < matchingComments.length; i++) {
let cm = matchingComments[i];
console.log(`after line: ${cm.diff_hunk_range.start + cm.position - 1 - 1}, ${'@' + cm.user.login}: '${cm.body}'`);
}
});
resolve(fileChanges);
});
});
} else {
if (this.repository.remotes && this.repository.remotes.length > 0) {
let promises = this.repository.remotes.map(remote => this.octo.repos(remote.owner, remote.name).pulls.fetch().then(prs => {
return prs.items.map(item => new PullRequest(item));
}));
return Promise.all(promises).then(values => {
let prs = [];
values.forEach(value => {
prs.push(...value);
});
return prs;
});
}
return [];
}
}
async provideComments(document: vscode.TextDocument): Promise<vscode.CommentThread[]> {
if (!this._comments) {
return [];
}
let matchingComments = getMatchingCommentsForDiffViewEditor(document.fileName, this._fileChanges, this._comments);
if (!matchingComments || !matchingComments.length) {
matchingComments = getMatchingCommentsForNormalEditor(document.fileName, this.workspaceRoot, this._comments);
}
if (!matchingComments || !matchingComments.length) {
return [];
}
let sections = _.groupBy(matchingComments, comment => comment.position);
let ret = [];
for (let i in sections) {
let comments = sections[i];
const comment = comments[0];
const pos = new vscode.Position(comment.diff_hunk_range.start + comment.position - 1 - 1, 0);
const range = new vscode.Range(pos, pos);
ret.push({
range,
comments: comments.map(comment => {
return {
body: new vscode.MarkdownString(comment.body),
userName: comment.user.login
};
})
});
}
return ret;
}
}
function parseComments(comments: any[]): Comment[] {
for (let i = 0; i < comments.length; i++) {
let diff_hunk = comments[i].diffHunk;
let hunk_info = DIFF_HUNK_INFO.exec(diff_hunk);
let oriStartLine = Number(hunk_info[1]);
let oriLen = Number(hunk_info[3]) | 0;
let startLine = Number(hunk_info[5]);
let len = Number(hunk_info[7]) | 0;
comments[i].diff_hunk_range = {
originalStart: oriStartLine,
originalLength: oriLen,
start: startLine,
length: len
};
}
return comments;
}
function getMatchingCommentsForDiffViewEditor(filePath: string, items: FileChangeItem[], comments: Comment[]): Comment[] {
let fileChangeItem = items.filter(item => filePath === path.resolve(item.workspaceRoot, item.filePath));
if (fileChangeItem.length === 0) {
return [];
} else {
let fileName = fileChangeItem[0].fileName;
let matchingComments = comments.filter(comment => comment.path === fileName);
return matchingComments;
}
}
function getMatchingCommentsForNormalEditor(filePath: string, workspaceRoot: string, comments: Comment[]): Comment[] {
// @todo, we should check commit id
let matchingComments = comments.filter(comment => path.resolve(workspaceRoot, comment.path) === filePath);
return matchingComments;
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// <reference path='../../../../src/vs/vscode.d.ts'/>
/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/>
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "out",
"noUnusedLocals": true,
"lib": [
"es6"
],
"sourceMap": true,
"rootDir": "./src",
"jsx": "react"
},
"exclude": [
"node_modules",
".vscode-test"
]
}
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/node@*":
version "9.6.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.2.tgz#e49ac1adb458835e95ca6487bc20f916b37aff23"
ajv@^5.1.0:
version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
dependencies:
co "^4.6.0"
fast-deep-equal "^1.0.0"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
asn1@~0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86"
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
aws4@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
bcrypt-pbkdf@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
dependencies:
tweetnacl "^0.14.3"
boom@4.x.x:
version "4.3.1"
resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31"
dependencies:
hoek "4.x.x"
boom@5.x.x:
version "5.2.0"
resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02"
dependencies:
hoek "4.x.x"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
checksum@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/checksum/-/checksum-0.1.1.tgz#dc6527d4c90be8560dbd1ed4cecf3297d528e9e9"
dependencies:
optimist "~0.3.5"
chownr@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
combined-stream@1.0.6, combined-stream@~1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
dependencies:
delayed-stream "~1.0.0"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
core-util-is@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
cryptiles@3.x.x:
version "3.1.2"
resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe"
dependencies:
boom "5.x.x"
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
dependencies:
assert-plus "^1.0.0"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
dugite@^1.28.0:
version "1.61.0"
resolved "https://registry.yarnpkg.com/dugite/-/dugite-1.61.0.tgz#d5dad047af478c1312f8b791eb56d9935e6fc47d"
dependencies:
checksum "^0.1.1"
mkdirp "^0.5.1"
progress "^2.0.0"
request "^2.83.0"
rimraf "^2.5.4"
tar "^4.0.2"
ecc-jsbn@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
dependencies:
jsbn "~0.1.0"
extend@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
extsprintf@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
extsprintf@^1.2.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
fast-deep-equal@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
fast-json-stable-stringify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
form-data@~2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
dependencies:
asynckit "^0.4.0"
combined-stream "1.0.6"
mime-types "^2.1.12"
fs-minipass@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d"
dependencies:
minipass "^2.2.1"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
dependencies:
assert-plus "^1.0.0"
glob@^7.0.5:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
har-validator@~5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd"
dependencies:
ajv "^5.1.0"
har-schema "^2.0.0"
hawk@~6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038"
dependencies:
boom "4.x.x"
cryptiles "3.x.x"
hoek "4.x.x"
sntp "2.x.x"
hoek@4.x.x:
version "4.2.1"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb"
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
dependencies:
assert-plus "^1.0.0"
jsprim "^1.2.2"
sshpk "^1.7.0"
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
json-schema-traverse@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
json-schema "0.2.3"
verror "1.10.0"
lodash@4.17.5, lodash@^4.16.4:
version "4.17.5"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
mime-db@~1.33.0:
version "1.33.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
mime-types@^2.1.12, mime-types@~2.1.17:
version "2.1.18"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8"
dependencies:
mime-db "~1.33.0"
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
dependencies:
brace-expansion "^1.1.7"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
minipass@^2.2.1, minipass@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.2.4.tgz#03c824d84551ec38a8d1bb5bc350a5a30a354a40"
dependencies:
safe-buffer "^5.1.1"
yallist "^3.0.0"
minizlib@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb"
dependencies:
minipass "^2.2.1"
mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
minimist "0.0.8"
oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
octokat@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/octokat/-/octokat-0.8.0.tgz#50841ca255743f91a715d11a1bde76f2eefcd97a"
dependencies:
lodash "^4.16.4"
xmlhttprequest "~1.8.0"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
dependencies:
wrappy "1"
optimist@~0.3.5:
version "0.3.7"
resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.3.7.tgz#c90941ad59e4273328923074d2cf2e7cbc6ec0d9"
dependencies:
wordwrap "~0.0.2"
os-tmpdir@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
progress@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
qs@~6.5.1:
version "6.5.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
request@^2.81.0, request@^2.83.0:
version "2.85.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa"
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.6.0"
caseless "~0.12.0"
combined-stream "~1.0.5"
extend "~3.0.1"
forever-agent "~0.6.1"
form-data "~2.3.1"
har-validator "~5.0.3"
hawk "~6.0.2"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.17"
oauth-sign "~0.8.2"
performance-now "^2.1.0"
qs "~6.5.1"
safe-buffer "^5.1.1"
stringstream "~0.0.5"
tough-cookie "~2.3.3"
tunnel-agent "^0.6.0"
uuid "^3.1.0"
rimraf@^2.5.4:
version "2.6.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
dependencies:
glob "^7.0.5"
safe-buffer@^5.0.1, safe-buffer@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
sntp@2.x.x:
version "2.1.0"
resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8"
dependencies:
hoek "4.x.x"
sshpk@^1.7.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb"
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
dashdash "^1.12.0"
getpass "^0.1.1"
optionalDependencies:
bcrypt-pbkdf "^1.0.0"
ecc-jsbn "~0.1.1"
jsbn "~0.1.0"
tweetnacl "~0.14.0"
stringstream@~0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
tar@^4.0.2:
version "4.4.1"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.1.tgz#b25d5a8470c976fd7a9a8a350f42c59e9fa81749"
dependencies:
chownr "^1.0.1"
fs-minipass "^1.2.5"
minipass "^2.2.4"
minizlib "^1.1.0"
mkdirp "^0.5.0"
safe-buffer "^5.1.1"
yallist "^3.0.2"
tmp@^0.0.31:
version "0.0.31"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
dependencies:
os-tmpdir "~1.0.1"
tough-cookie@~2.3.3:
version "2.3.4"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655"
dependencies:
punycode "^1.4.1"
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
dependencies:
safe-buffer "^5.0.1"
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
typescript@^2.1.4:
version "2.8.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.1.tgz#6160e4f8f195d5ba81d4876f9c0cc1fbc0820624"
uuid@^3.1.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
dependencies:
assert-plus "^1.0.0"
core-util-is "1.0.2"
extsprintf "^1.2.0"
wordwrap@~0.0.2:
version "0.0.3"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
xmlhttprequest@~1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc"
yallist@^3.0.0, yallist@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9"
......@@ -936,6 +936,22 @@ export interface Command {
tooltip?: string;
arguments?: any[];
}
export interface CommentThread {
readonly range: IRange;
readonly comments: Comment[];
}
export interface Comment {
readonly body: IMarkdownString;
readonly userName: string;
}
export interface CommentProvider {
provideComments(model: model.ITextModel, token: CancellationToken): CommentThread[];
}
export interface ICodeLensSymbol {
range: IRange;
id?: string;
......
......@@ -5,6 +5,7 @@
'use strict';
import 'vs/css!./review';
import * as modes from 'vs/editor/common/modes';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IViewZone } from 'vs/editor/browser/editorBrowser';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
......@@ -13,10 +14,8 @@ import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { TrackedRangeStickiness } from 'vs/editor/common/model';
import { ZoneWidget, IOptions } from '../zoneWidget/zoneWidget';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { renderMarkdown } from 'vs/base/browser/htmlContentRenderer';
import { IComment, getComments } from 'vs/editor/contrib/review/reviewProvider';
import { RawContextKey, IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
......@@ -67,7 +66,7 @@ export class ReviewZoneWidget extends ZoneWidget {
container.appendChild(this._domNode);
}
display(comments: IComment[], lineNumber: number) {
display(comments: modes.Comment[], lineNumber: number) {
this.show({ lineNumber: lineNumber, column: 1 }, 2);
for (let i = 0; i < comments.length; i++) {
......@@ -76,21 +75,21 @@ export class ReviewZoneWidget extends ZoneWidget {
let header = document.createElement('h4');
let author = document.createElement('strong');
author.className = 'author';
author.innerText = comments[i].user;
let time = document.createElement('span');
time.className = 'created_at';
time.innerText = comments[i].created_at;
author.innerText = comments[i].userName;
// let time = document.createElement('span');
// time.className = 'created_at';
// time.innerText = comments[i].created_at;
header.appendChild(author);
header.appendChild(time);
// header.appendChild(time);
singleCommentContainer.appendChild(header);
let body = document.createElement('div');
body.className = 'comment-body';
singleCommentContainer.appendChild(body);
let md = new MarkdownString(comments[i].body);
let md = comments[i].body;
body.appendChild(renderMarkdown(md));
this._domNode.appendChild(singleCommentContainer);
// this._domNode.appendChild(document.createElement('textarea'));
}
// this._domNode.appendChild(document.createElement('textarea'));
this._resizeObserver = new ResizeObserver(entries => {
if (entries[0].target === this._domNode) {
const lineHeight = this.editor.getConfiguration().lineHeight;
......@@ -111,7 +110,6 @@ export class ReviewZoneWidget extends ZoneWidget {
}
export class ReviewController implements IEditorContribution {
private globalToDispose: IDisposable[];
private localToDispose: IDisposable[];
private editor: ICodeEditor;
......@@ -119,16 +117,18 @@ export class ReviewController implements IEditorContribution {
private _domNode: HTMLElement;
private _zoneWidget: ReviewZoneWidget;
private _reviewPanelVisible: IContextKey<boolean>;
private _commentThreads: modes.CommentThread[];
constructor(
editor: ICodeEditor,
@IContextKeyService contextKeyService: IContextKeyService,
@IContextKeyService contextKeyService: IContextKeyService
) {
this.editor = editor;
this.globalToDispose = [];
this.localToDispose = [];
this.decorationIDs = [];
this.mouseDownInfo = null;
this._commentThreads = [];
this._reviewPanelVisible = ctxReviewPanelVisible.bindTo(contextKeyService);
this._domNode = document.createElement('div');
......@@ -161,20 +161,6 @@ export class ReviewController implements IEditorContribution {
this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e)));
this.localToDispose.push(this.editor.onMouseUp(e => this.onEditorMouseUp(e)));
this.editor.changeDecorations(accessor => {
this.decorationIDs = accessor.deltaDecorations(this.decorationIDs, [
{
range: {
startLineNumber: 6,
startColumn: 1,
endLineNumber: 6,
endColumn: 1
},
options: REVIEWL_DECORATION
}
]);
});
}
private mouseDownInfo: { lineNumber: number, iconClicked: boolean };
......@@ -232,9 +218,30 @@ export class ReviewController implements IEditorContribution {
this._reviewPanelVisible.set(true);
this._zoneWidget = new ReviewZoneWidget(this.editor);
this._zoneWidget.display(getComments(), lineNumber);
this._zoneWidget.display(this.getComments(lineNumber), lineNumber);
}
getComments(line: number): modes.Comment[] {
for (let i = 0; i < this._commentThreads.length; i++) {
if (this._commentThreads[i].range.startLineNumber === line) {
return this._commentThreads[i].comments;
}
}
return [];
}
setComments(commentThreads: modes.CommentThread[]): void {
this._commentThreads = commentThreads;
this.editor.changeDecorations(accessor => {
this.decorationIDs = accessor.deltaDecorations(this.decorationIDs, commentThreads.map(thread => ({
range: thread.range,
options: REVIEWL_DECORATION
})));
});
}
public closeWidget(): void {
this._reviewPanelVisible.reset();
......
......@@ -4998,6 +4998,20 @@ declare namespace monaco.languages {
arguments?: any[];
}
export interface CommentThread {
readonly range: IRange;
readonly comments: Comment[];
}
export interface Comment {
readonly body: IMarkdownString;
readonly userName: string;
}
export interface CommentProvider {
provideComments(model: editor.ITextModel, token: CancellationToken): CommentThread[];
}
export interface ICodeLensSymbol {
range: IRange;
id?: string;
......
......@@ -723,4 +723,26 @@ declare module 'vscode' {
}
//#endregion
interface CommentThread {
range: Range;
comments: Comment[];
}
interface Comment {
body: MarkdownString;
userName: string;
}
/**
* TODO: force update event?
* TODO: resolve step?
*/
interface CommentProvider {
provideComments(document: TextDocument, token: CancellationToken): Promise<CommentThread[]>;
}
namespace workspace {
export function registerCommentProvider(provider: CommentProvider): Disposable;
}
}
......@@ -48,6 +48,7 @@ import './mainThreadTerminalService';
import './mainThreadTreeViews';
import './mainThreadLogService';
import './mainThreadWebview';
import './mainThreadComments';
import './mainThreadWindow';
import './mainThreadWorkspace';
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ITextModel } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import { ReviewController } from 'vs/editor/contrib/review/review';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { keys } from '../../../base/common/map';
import { IWorkbenchEditorService } from '../../services/editor/common/editorService';
import { ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape } from '../node/extHost.protocol';
@extHostNamedCustomer(MainContext.MainThreadComments)
export class MainThreadComments extends Disposable implements MainThreadCommentsShape {
private _proxy: ExtHostCommentsShape;
private _providers = new Map<number, IDisposable>();
constructor(
extHostContext: IExtHostContext,
@IEditorGroupService editorGroupService: IEditorGroupService,
@IWorkbenchEditorService workbenchEditorService: IWorkbenchEditorService,
@ICodeEditorService private _codeEditorService: ICodeEditorService
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments);
editorGroupService.onEditorsChanged(e => {
const outerEditor = this.getFocusedEditor();
if (!outerEditor) {
return;
}
const controller = ReviewController.get(outerEditor);
if (!controller) {
return;
}
this.provideComments(outerEditor.getModel()).then(commentThreads => {
controller.setComments(commentThreads);
});
});
}
$registerCommentProvider(handle: number): void {
this._providers.set(handle, undefined);
}
$unregisterCommentProvider(handle: number): void {
throw new Error('Method not implemented.');
}
dispose(): void {
throw new Error('Method not implemented.');
}
getFocusedEditor(): ICodeEditor {
let editor = this._codeEditorService.getFocusedCodeEditor();
// if\
return editor;
}
async provideComments(model: ITextModel): Promise<modes.CommentThread[]> {
const result: modes.CommentThread[] = [];
for (const handle of keys(this._providers)) {
result.push(...await this._proxy.$providerComments(handle, model.uri));
}
return result;
}
}
......@@ -58,6 +58,7 @@ import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { OverviewRulerLane } from 'vs/editor/common/model';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import { ExtHostWebviews } from 'vs/workbench/api/node/extHostWebview';
import { ExtHostComments } from './extHostComments';
export interface IExtensionApiFactory {
(extension: IExtensionDescription): typeof vscode;
......@@ -119,6 +120,7 @@ export function createApiFactory(
const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
const exthostCommentProviders = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostDocuments));
// Check that no named customers are missing
const expected: ProxyIdentifier<any>[] = Object.keys(ExtHostContext).map((key) => ExtHostContext[key]);
......@@ -540,6 +542,9 @@ export function createApiFactory(
}),
registerSearchProvider: proposedApiFunction(extension, (scheme, provider) => {
return extHostFileSystem.registerSearchProvider(scheme, provider);
}),
registerCommentProvider: proposedApiFunction(extension, (provider: vscode.CommentProvider) => {
return exthostCommentProviders.registerCommentProvider(provider);
})
};
......
......@@ -104,6 +104,11 @@ export interface MainThreadCommandsShape extends IDisposable {
$getCommands(): Thenable<string[]>;
}
export interface MainThreadCommentsShape extends IDisposable {
$registerCommentProvider(handle: number): void;
$unregisterCommentProvider(handle: number): void;
}
export interface MainThreadConfigurationShape extends IDisposable {
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any, resource: UriComponents): TPromise<void>;
$removeConfigurationOption(target: ConfigurationTarget, key: string, resource: UriComponents): TPromise<void>;
......@@ -813,10 +818,15 @@ export interface ExtHostProgressShape {
$acceptProgressCanceled(handle: number): void;
}
export interface ExtHostCommentsShape {
$providerComments(handle: number, document: UriComponents): TPromise<modes.CommentThread[]>;
}
// --- proxy identifiers
export const MainContext = {
MainThreadCommands: <ProxyIdentifier<MainThreadCommandsShape>>createMainId<MainThreadCommandsShape>('MainThreadCommands'),
MainThreadComments: createMainId<MainThreadCommentsShape>('MainThreadComments'),
MainThreadConfiguration: createMainId<MainThreadConfigurationShape>('MainThreadConfiguration'),
MainThreadDebugService: createMainId<MainThreadDebugServiceShape>('MainThreadDebugService'),
MainThreadDecorations: createMainId<MainThreadDecorationsShape>('MainThreadDecorations'),
......@@ -871,5 +881,6 @@ export const ExtHostContext = {
ExtHostWorkspace: createExtId<ExtHostWorkspaceShape>('ExtHostWorkspace'),
ExtHostWindow: createExtId<ExtHostWindowShape>('ExtHostWindow'),
ExtHostWebviews: createExtId<ExtHostWebviewsShape>('ExtHostWebviews'),
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress')
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress'),
ExtHostComments: createMainId<ExtHostCommentsShape>('ExtHostComments')
};
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { asWinJsPromise } from 'vs/base/common/async';
import { values } from 'vs/base/common/map';
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import * as modes from 'vs/editor/common/modes';
import { ExtHostDocuments } from 'vs/workbench/api/node/extHostDocuments';
import * as vscode from 'vscode';
import { flatten } from '../../../base/common/arrays';
import { ExtHostCommentsShape, IMainContext, MainContext, MainThreadCommentsShape } from './extHost.protocol';
import * as extHostTypeConverter from 'vs/workbench/api/node/extHostTypeConverters';
export class ExtHostComments implements ExtHostCommentsShape {
private static handlePool = 0;
private _proxy: MainThreadCommentsShape;
private _providers = new Map<number, vscode.CommentProvider>();
constructor(
mainContext: IMainContext,
private readonly _documents: ExtHostDocuments,
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadComments);
}
registerCommentProvider(
provider: vscode.CommentProvider
): vscode.Disposable {
const handle = ExtHostComments.handlePool++;
this._providers.set(handle, provider);
this._proxy.$registerCommentProvider(handle);
return {
dispose: () => {
this._proxy.$unregisterCommentProvider(handle);
this._providers.delete(handle);
}
};
}
$providerComments(handle: number, uri: UriComponents): TPromise<modes.CommentThread[]> {
const data = this._documents.getDocumentData(URI.revive(uri));
if (!data || !data.document) {
return TPromise.as([]);
}
return asWinJsPromise(token => {
const allProviderResults = values(this._providers).map(provider => provider.provideComments(data.document, token));
return TPromise.join(allProviderResults);
})
.then(flatten)
.then(comments => comments.map(convertCommentThread));
}
}
function convertCommentThread(vscodeCommentThread: vscode.CommentThread): modes.CommentThread {
return {
range: extHostTypeConverter.fromRange(vscodeCommentThread.range),
comments: vscodeCommentThread.comments.map(convertComment)
};
}
function convertComment(vscodeComment: vscode.Comment): modes.Comment {
return {
body: extHostTypeConverter.MarkdownString.from(vscodeComment.body),
userName: vscodeComment.userName
};
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册